From 35c02bc5c672a14d0ef3722b4153f4769fdd6edc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ella=20Van=C2=A0Dorpe?= Date: Tue, 30 Oct 2018 17:50:50 +0100 Subject: [PATCH 01/21] Delay TinyMCE initialisation to focus (#10723) --- .../button/test/__snapshots__/index.js.snap | 6 +++- .../heading/test/__snapshots__/index.js.snap | 6 +++- .../src/list/test/__snapshots__/index.js.snap | 6 +++- .../test/__snapshots__/index.js.snap | 6 +++- .../test/__snapshots__/index.js.snap | 6 +++- .../test/__snapshots__/index.js.snap | 6 +++- packages/block-library/src/quote/index.js | 5 ++-- .../quote/test/__snapshots__/index.js.snap | 6 +++- .../test/__snapshots__/index.js.snap | 12 ++++++-- .../verse/test/__snapshots__/index.js.snap | 6 +++- .../src/components/rich-text/tinymce.js | 28 ++++++++++++++++++- test/e2e/specs/block-deletion.test.js | 4 +++ .../blocks/__snapshots__/quote.test.js.snap | 6 +++- test/e2e/specs/splitting-merging.test.js | 6 ++++ test/e2e/support/utils.js | 2 +- 15 files changed, 95 insertions(+), 16 deletions(-) diff --git a/packages/block-library/src/button/test/__snapshots__/index.js.snap b/packages/block-library/src/button/test/__snapshots__/index.js.snap index fa8009e4cbc03e..9d96ec60114b22 100644 --- a/packages/block-library/src/button/test/__snapshots__/index.js.snap +++ b/packages/block-library/src/button/test/__snapshots__/index.js.snap @@ -23,7 +23,11 @@ exports[`core/button block edit matches snapshot 1`] = ` contenteditable="true" data-is-placeholder-visible="true" role="textbox" - /> + > +
+
diff --git a/packages/block-library/src/quote/index.js b/packages/block-library/src/quote/index.js index a87b0dba274534..49eef577f907ad 100644 --- a/packages/block-library/src/quote/index.js +++ b/packages/block-library/src/quote/index.js @@ -115,7 +115,7 @@ export const settings = { blocks: [ 'core/paragraph' ], transform: ( { value, citation } ) => { const paragraphs = []; - if ( value ) { + if ( value && value !== '

' ) { paragraphs.push( ...split( create( { html: value, multilineTag: 'p' } ), '\u2028' ) .map( ( piece ) => @@ -125,7 +125,7 @@ export const settings = { ) ); } - if ( citation ) { + if ( citation && citation !== '

' ) { paragraphs.push( createBlock( 'core/paragraph', { content: citation, @@ -189,7 +189,6 @@ export const settings = { edit( { attributes, setAttributes, isSelected, mergeBlocks, onReplace, className } ) { const { align, value, citation } = attributes; - return ( diff --git a/packages/block-library/src/quote/test/__snapshots__/index.js.snap b/packages/block-library/src/quote/test/__snapshots__/index.js.snap index 813b6d8417cb66..54f0293d643a5d 100644 --- a/packages/block-library/src/quote/test/__snapshots__/index.js.snap +++ b/packages/block-library/src/quote/test/__snapshots__/index.js.snap @@ -21,7 +21,11 @@ exports[`core/quote block edit matches snapshot 1`] = ` contenteditable="true" data-is-placeholder-visible="true" role="textbox" - /> + > +
+
diff --git a/packages/block-library/src/text-columns/test/__snapshots__/index.js.snap b/packages/block-library/src/text-columns/test/__snapshots__/index.js.snap index b618bdb565e07f..19e26bedd4e4f5 100644 --- a/packages/block-library/src/text-columns/test/__snapshots__/index.js.snap +++ b/packages/block-library/src/text-columns/test/__snapshots__/index.js.snap @@ -24,7 +24,11 @@ exports[`core/text-columns block edit matches snapshot 1`] = ` contenteditable="true" data-is-placeholder-visible="true" role="textbox" - /> + > +
+

@@ -55,7 +59,11 @@ exports[`core/text-columns block edit matches snapshot 1`] = ` contenteditable="true" data-is-placeholder-visible="true" role="textbox" - /> + > +
+

diff --git a/packages/block-library/src/verse/test/__snapshots__/index.js.snap b/packages/block-library/src/verse/test/__snapshots__/index.js.snap index dadd75177de95d..4141acd82063f3 100644 --- a/packages/block-library/src/verse/test/__snapshots__/index.js.snap +++ b/packages/block-library/src/verse/test/__snapshots__/index.js.snap @@ -18,7 +18,11 @@ exports[`core/verse block edit matches snapshot 1`] = ` contenteditable="true" data-is-placeholder-visible="true" role="textbox" - /> + > +
+

diff --git a/packages/editor/src/components/rich-text/tinymce.js b/packages/editor/src/components/rich-text/tinymce.js
index 25b3a2ac860326..c589dde327075d 100644
--- a/packages/editor/src/components/rich-text/tinymce.js
+++ b/packages/editor/src/components/rich-text/tinymce.js
@@ -101,9 +101,10 @@ export default class TinyMCE extends Component {
 	constructor() {
 		super();
 		this.bindEditorNode = this.bindEditorNode.bind( this );
+		this.onFocus = this.onFocus.bind( this );
 	}
 
-	componentDidMount() {
+	onFocus() {
 		this.initialize();
 	}
 
@@ -158,6 +159,11 @@ export default class TinyMCE extends Component {
 			browser_spellcheck: true,
 			entity_encoding: 'raw',
 			convert_urls: false,
+			// Disables TinyMCE's parsing to verify HTML. It makes
+			// initialisation a bit faster. Since we're setting raw HTML
+			// already with dangerouslySetInnerHTML, we don't need this to be
+			// verified.
+			verify_html: false,
 			inline_boundaries_selector: 'a[href],code,b,i,strong,em,del,ins,sup,sub',
 			plugins: [],
 		} );
@@ -169,6 +175,18 @@ export default class TinyMCE extends Component {
 				this.editor = editor;
 				this.props.onSetup( editor );
 
+				// TinyMCE resets the element content on initialization, even
+				// when it's already identical to what exists currently. This
+				// behavior clobbers a selection which exists at the time of
+				// initialization, thus breaking writing flow navigation. The
+				// hack here neutralizes setHTML during initialization.
+				let setHTML;
+
+				editor.on( 'preinit', () => {
+					setHTML = editor.dom.setHTML;
+					editor.dom.setHTML = () => {};
+				} );
+
 				editor.on( 'init', () => {
 					// See https://github.com/tinymce/tinymce/blob/master/src/core/main/ts/keyboard/FormatShortcuts.ts
 					[ 'b', 'i', 'u' ].forEach( ( character ) => {
@@ -177,6 +195,8 @@ export default class TinyMCE extends Component {
 					[ 1, 2, 3, 4, 5, 6, 7, 8, 9 ].forEach( ( number ) => {
 						editor.shortcuts.remove( `access+${ number }` );
 					} );
+
+					editor.dom.setHTML = setHTML;
 				} );
 			},
 		} );
@@ -247,6 +267,11 @@ export default class TinyMCE extends Component {
 			} );
 		}
 
+		if ( initialHTML === '' ) {
+			// Ensure the field is ready to receive focus by TinyMCE.
+			initialHTML = '
'; + } + return createElement( tagName, { ...ariaProps, className: classnames( className, 'editor-rich-text__tinymce' ), @@ -258,6 +283,7 @@ export default class TinyMCE extends Component { dangerouslySetInnerHTML: { __html: initialHTML }, onPaste, onInput, + onFocus: this.onFocus, } ); } } diff --git a/test/e2e/specs/block-deletion.test.js b/test/e2e/specs/block-deletion.test.js index 7da0695f1ab436..fb6dd2b40abd5b 100644 --- a/test/e2e/specs/block-deletion.test.js +++ b/test/e2e/specs/block-deletion.test.js @@ -7,6 +7,7 @@ import { newPost, pressWithModifier, ACCESS_MODIFIER_KEYS, + waitForRichTextInitialization, } from '../support/utils'; const addThreeParagraphsToNewPost = async () => { @@ -16,8 +17,10 @@ const addThreeParagraphsToNewPost = async () => { await clickBlockAppender(); await page.keyboard.type( 'First paragraph' ); await page.keyboard.press( 'Enter' ); + await waitForRichTextInitialization(); await page.keyboard.type( 'Second paragraph' ); await page.keyboard.press( 'Enter' ); + await waitForRichTextInitialization(); }; const clickOnBlockSettingsMenuItem = async ( buttonLabel ) => { @@ -96,6 +99,7 @@ describe( 'block deletion -', () => { // Add a third paragraph for this test. await page.keyboard.type( 'Third paragraph' ); await page.keyboard.press( 'Enter' ); + await waitForRichTextInitialization(); // Press the up arrow once to select the third and fourth blocks. await pressWithModifier( 'Shift', 'ArrowUp' ); diff --git a/test/e2e/specs/blocks/__snapshots__/quote.test.js.snap b/test/e2e/specs/blocks/__snapshots__/quote.test.js.snap index 6a46b767390efc..5257716d67d00c 100644 --- a/test/e2e/specs/blocks/__snapshots__/quote.test.js.snap +++ b/test/e2e/specs/blocks/__snapshots__/quote.test.js.snap @@ -58,7 +58,11 @@ exports[`Quote can be converted to paragraphs and renders a paragraph for the ci " `; -exports[`Quote can be converted to paragraphs and renders a void paragraph if both the cite and quote are void 1`] = `""`; +exports[`Quote can be converted to paragraphs and renders a void paragraph if both the cite and quote are void 1`] = ` +" +

+" +`; exports[`Quote can be converted to paragraphs and renders one paragraph block per

within quote 1`] = ` " diff --git a/test/e2e/specs/splitting-merging.test.js b/test/e2e/specs/splitting-merging.test.js index d059fbf42ea63d..fca2a8820a0135 100644 --- a/test/e2e/specs/splitting-merging.test.js +++ b/test/e2e/specs/splitting-merging.test.js @@ -8,6 +8,7 @@ import { pressTimes, pressWithModifier, META_KEY, + waitForRichTextInitialization, } from '../support/utils'; describe( 'splitting and merging blocks', () => { @@ -132,7 +133,9 @@ describe( 'splitting and merging blocks', () => { await pressWithModifier( META_KEY, 'b' ); await page.keyboard.press( 'ArrowRight' ); await page.keyboard.press( 'Enter' ); + await waitForRichTextInitialization(); await page.keyboard.press( 'Enter' ); + await waitForRichTextInitialization(); await page.keyboard.press( 'Backspace' ); @@ -172,8 +175,11 @@ describe( 'splitting and merging blocks', () => { await insertBlock( 'Paragraph' ); await page.keyboard.type( 'First' ); await page.keyboard.press( 'Enter' ); + await waitForRichTextInitialization(); await page.keyboard.press( 'Enter' ); + await waitForRichTextInitialization(); await page.keyboard.press( 'Enter' ); + await waitForRichTextInitialization(); await page.keyboard.type( 'Second' ); await page.keyboard.press( 'ArrowUp' ); await page.keyboard.press( 'ArrowUp' ); diff --git a/test/e2e/support/utils.js b/test/e2e/support/utils.js index 4ac62bf7372991..7275da4d1d2fcf 100644 --- a/test/e2e/support/utils.js +++ b/test/e2e/support/utils.js @@ -100,7 +100,7 @@ async function login() { * @return {Promise} Promise resolving once RichText is initialized, or is * determined to not be a container of the active element. */ -async function waitForRichTextInitialization() { +export async function waitForRichTextInitialization() { const isInRichText = await page.evaluate( () => { return !! document.activeElement.closest( '.editor-rich-text__tinymce' ); } ); From 393f5baa1cb640ea142c70458fdd22b8d8d2334e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ella=20Van=C2=A0Dorpe?= Date: Tue, 30 Oct 2018 17:51:04 +0100 Subject: [PATCH 02/21] RichText: fix format placeholder (#11102) * Fix format placeholder * Unduplicate logic --- packages/rich-text/src/apply-format.js | 18 ++++++++------ packages/rich-text/src/test/apply-format.js | 12 +++++----- packages/rich-text/src/to-dom.js | 1 + packages/rich-text/src/to-tree.js | 24 ++++++++++++++++++- .../__snapshots__/rich-text.test.js.snap | 6 +++++ test/e2e/specs/rich-text.test.js | 13 ++++++++++ 6 files changed, 60 insertions(+), 14 deletions(-) diff --git a/packages/rich-text/src/apply-format.js b/packages/rich-text/src/apply-format.js index ebdf6ecaecfea3..7580ebaee2086a 100644 --- a/packages/rich-text/src/apply-format.js +++ b/packages/rich-text/src/apply-format.js @@ -2,15 +2,13 @@ * External dependencies */ -import { find, reject } from 'lodash'; +import { find } from 'lodash'; /** * Internal dependencies */ import { normaliseFormats } from './normalise-formats'; -import { insert } from './insert'; -import { ZERO_WIDTH_NO_BREAK_SPACE } from './special-characters'; /** * Apply a format object to a Rich Text value from the given `startIndex` to the @@ -56,10 +54,16 @@ export function applyFormat( const previousFormat = newFormats[ startIndex - 1 ] || []; const hasType = find( previousFormat, { type: format.type } ); - return insert( { formats, text, start, end }, { - formats: hasType ? [ reject( previousFormat, { type: format.type } ) ] : [ [ ...previousFormat, format ] ], - text: ZERO_WIDTH_NO_BREAK_SPACE, - } ); + return { + formats, + text, + start, + end, + formatPlaceholder: { + index: startIndex, + format: hasType ? undefined : format, + }, + }; } } else { for ( let index = startIndex; index < endIndex; index++ ) { diff --git a/packages/rich-text/src/test/apply-format.js b/packages/rich-text/src/test/apply-format.js index 73416cdb5cfacc..3aa51c927e71c5 100644 --- a/packages/rich-text/src/test/apply-format.js +++ b/packages/rich-text/src/test/apply-format.js @@ -8,7 +8,6 @@ import deepFreeze from 'deep-freeze'; */ import { applyFormat } from '../apply-format'; -import { ZERO_WIDTH_NO_BREAK_SPACE } from '../special-characters'; import { getSparseArrayLength } from './helpers'; describe( 'applyFormat', () => { @@ -61,16 +60,17 @@ describe( 'applyFormat', () => { end: 0, }; const expected = { - formats: [ [ a2 ], , , , , [ a ], [ a ], [ a ], , , , , , , ], - text: `${ ZERO_WIDTH_NO_BREAK_SPACE }one two three`, - start: 1, - end: 1, + ...record, + formatPlaceholder: { + format: a2, + index: 0, + }, }; const result = applyFormat( deepFreeze( record ), a2 ); expect( result ).toEqual( expected ); expect( result ).not.toBe( record ); - expect( getSparseArrayLength( result.formats ) ).toBe( 4 ); + expect( getSparseArrayLength( result.formats ) ).toBe( 3 ); } ); it( 'should apply format on existing format if selection is collapsed', () => { diff --git a/packages/rich-text/src/to-dom.js b/packages/rich-text/src/to-dom.js index 319e8b61ee1377..b5851817b5db7f 100644 --- a/packages/rich-text/src/to-dom.js +++ b/packages/rich-text/src/to-dom.js @@ -159,6 +159,7 @@ export function toDom( { onEndIndex( body, pointer ) { endPath = createPathToNode( pointer, body, [ pointer.nodeValue.length ] ); }, + isEditableTree: true, } ); if ( createLinePadding ) { diff --git a/packages/rich-text/src/to-tree.js b/packages/rich-text/src/to-tree.js index d599db049ff9aa..893b331c44a37e 100644 --- a/packages/rich-text/src/to-tree.js +++ b/packages/rich-text/src/to-tree.js @@ -6,6 +6,7 @@ import { getFormatType } from './get-format-type'; import { LINE_SEPARATOR, OBJECT_REPLACEMENT_CHARACTER, + ZERO_WIDTH_NO_BREAK_SPACE, } from './special-characters'; function fromFormat( { type, attributes, object } ) { @@ -55,8 +56,9 @@ export function toTree( { appendText, onStartIndex, onEndIndex, + isEditableTree, } ) { - const { formats, text, start, end } = value; + const { formats, text, start, end, formatPlaceholder } = value; const formatsLength = formats.length + 1; const tree = createEmpty(); const multilineFormat = { type: multilineTag }; @@ -73,6 +75,22 @@ export function toTree( { append( tree, '' ); } + function setFormatPlaceholder( pointer, index ) { + if ( isEditableTree && formatPlaceholder && formatPlaceholder.index === index ) { + const parent = getParent( pointer ); + + if ( formatPlaceholder.format === undefined ) { + pointer = getParent( parent ); + } else { + pointer = append( parent, fromFormat( formatPlaceholder.format ) ); + } + + pointer = append( pointer, ZERO_WIDTH_NO_BREAK_SPACE ); + } + + return pointer; + } + for ( let i = 0; i < formatsLength; i++ ) { const character = text.charAt( i ); let characterFormats = formats[ i ]; @@ -146,6 +164,8 @@ export function toTree( { continue; } + pointer = setFormatPlaceholder( pointer, 0 ); + // If there is selection at 0, handle it before characters are inserted. if ( i === 0 ) { if ( onStartIndex && start === 0 ) { @@ -169,6 +189,8 @@ export function toTree( { } } + pointer = setFormatPlaceholder( pointer, i + 1 ); + if ( onStartIndex && start === i + 1 ) { onStartIndex( tree, pointer ); } diff --git a/test/e2e/specs/__snapshots__/rich-text.test.js.snap b/test/e2e/specs/__snapshots__/rich-text.test.js.snap index 2ddafbae544f22..fc96d2d6f1fab5 100644 --- a/test/e2e/specs/__snapshots__/rich-text.test.js.snap +++ b/test/e2e/specs/__snapshots__/rich-text.test.js.snap @@ -1,5 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`RichText should apply formatting when selection is collapsed 1`] = ` +" +

SomeĀ bold.

+" +`; + exports[`RichText should apply formatting with access shortcut 1`] = ` "

test

diff --git a/test/e2e/specs/rich-text.test.js b/test/e2e/specs/rich-text.test.js index 398196d0a37db7..9203ec32c303dd 100644 --- a/test/e2e/specs/rich-text.test.js +++ b/test/e2e/specs/rich-text.test.js @@ -45,4 +45,17 @@ describe( 'RichText', () => { expect( await getEditedPostContent() ).toMatchSnapshot(); } ); + + it( 'should apply formatting when selection is collapsed', async () => { + await clickBlockAppender(); + await page.keyboard.type( 'Some ' ); + // All following characters should now be bold. + await pressWithModifier( META_KEY, 'b' ); + await page.keyboard.type( 'bold' ); + // All following characters should no longer be bold. + await pressWithModifier( META_KEY, 'b' ); + await page.keyboard.type( '.' ); + + expect( await getEditedPostContent() ).toMatchSnapshot(); + } ); } ); From 6e6c8d5f47d7d17dc808cdede8eb026031fabc1b Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Tue, 30 Oct 2018 13:28:28 -0400 Subject: [PATCH 03/21] Editor: Optimize Inserter props generation and reconciliation (#11243) --- .../test/__snapshots__/index.js.snap | 6 +- .../editor/src/components/inserter/index.js | 87 ++++++++++--------- .../editor/src/components/inserter/menu.js | 57 ++++++++++-- 3 files changed, 95 insertions(+), 55 deletions(-) diff --git a/packages/editor/src/components/default-block-appender/test/__snapshots__/index.js.snap b/packages/editor/src/components/default-block-appender/test/__snapshots__/index.js.snap index 46c017374b5149..71484219b7d98e 100644 --- a/packages/editor/src/components/default-block-appender/test/__snapshots__/index.js.snap +++ b/packages/editor/src/components/default-block-appender/test/__snapshots__/index.js.snap @@ -23,7 +23,7 @@ exports[`DefaultBlockAppender should append a default block when input focused 1 value="Write your story" /> -
@@ -45,7 +45,7 @@ exports[`DefaultBlockAppender should match snapshot 1`] = ` value="Write your story" /> - @@ -67,7 +67,7 @@ exports[`DefaultBlockAppender should optionally show without prompt 1`] = ` value="" /> - diff --git a/packages/editor/src/components/inserter/index.js b/packages/editor/src/components/inserter/index.js index d10f70e315af2c..1879683b3c072a 100644 --- a/packages/editor/src/components/inserter/index.js +++ b/packages/editor/src/components/inserter/index.js @@ -3,10 +3,9 @@ */ import { __ } from '@wordpress/i18n'; import { Dropdown, IconButton } from '@wordpress/components'; -import { createBlock, isUnmodifiedDefaultBlock } from '@wordpress/blocks'; import { Component } from '@wordpress/element'; -import { withSelect, withDispatch } from '@wordpress/data'; -import { compose } from '@wordpress/compose'; +import { withSelect } from '@wordpress/data'; +import { compose, ifCondition } from '@wordpress/compose'; /** * Internal dependencies @@ -30,6 +29,8 @@ class Inserter extends Component { super( ...arguments ); this.onToggle = this.onToggle.bind( this ); + this.renderToggle = this.renderToggle.bind( this ); + this.renderContent = this.renderContent.bind( this ); } onToggle( isOpen ) { @@ -41,20 +42,46 @@ class Inserter extends Component { } } - render() { + /** + * Render callback to display Dropdown toggle element. + * + * @param {Function} options.onToggle Callback to invoke when toggle is + * pressed. + * @param {boolean} options.isOpen Whether dropdown is currently open. + * + * @return {WPElement} Dropdown toggle element. + */ + renderToggle( { onToggle, isOpen } ) { const { - items, - position, - title, - onInsertBlock, - rootClientId, disabled, renderToggle = defaultRenderToggle, } = this.props; - if ( items.length === 0 ) { - return null; - } + return renderToggle( { onToggle, isOpen, disabled } ); + } + + /** + * Render callback to display Dropdown content element. + * + * @param {Function} options.onClose Callback to invoke when dropdown is + * closed. + * + * @return {WPElement} Dropdown content element. + */ + renderContent( { onClose } ) { + const { rootClientId, index } = this.props; + + return ( + + ); + } + + render() { + const { position, title } = this.props; return ( renderToggle( { onToggle, isOpen, disabled } ) } - renderContent={ ( { onClose } ) => { - const onSelect = ( item ) => { - onInsertBlock( item ); - - onClose(); - }; - - return ( - - ); - } } + renderToggle={ this.renderToggle } + renderContent={ this.renderContent } /> ); } @@ -90,7 +103,6 @@ export default compose( [ const { getEditedPostAttribute, getBlockInsertionPoint, - getSelectedBlock, getInserterItems, } = select( 'core/editor' ); @@ -106,21 +118,10 @@ export default compose( [ return { title: getEditedPostAttribute( 'title' ), - selectedBlock: getSelectedBlock(), - items: getInserterItems( rootClientId ), - index, + hasItems: getInserterItems( rootClientId ).length > 0, rootClientId, + index, }; } ), - withDispatch( ( dispatch, ownProps ) => ( { - onInsertBlock: ( item ) => { - const { selectedBlock, index, rootClientId } = ownProps; - const { name, initialAttributes } = item; - const insertedBlock = createBlock( name, initialAttributes ); - if ( selectedBlock && isUnmodifiedDefaultBlock( selectedBlock ) ) { - return dispatch( 'core/editor' ).replaceBlocks( selectedBlock.clientId, insertedBlock ); - } - return dispatch( 'core/editor' ).insertBlock( insertedBlock, index, rootClientId ); - }, - } ) ), + ifCondition( ( { hasItems } ) => hasItems ), ] )( Inserter ); diff --git a/packages/editor/src/components/inserter/menu.js b/packages/editor/src/components/inserter/menu.js index 4bb2362377c390..9780661f6442f9 100644 --- a/packages/editor/src/components/inserter/menu.js +++ b/packages/editor/src/components/inserter/menu.js @@ -23,7 +23,12 @@ import scrollIntoView from 'dom-scroll-into-view'; import { __, _n, _x, sprintf } from '@wordpress/i18n'; import { Component, findDOMNode, createRef } from '@wordpress/element'; import { withSpokenMessages, PanelBody } from '@wordpress/components'; -import { getCategories, isReusableBlock } from '@wordpress/blocks'; +import { + getCategories, + isReusableBlock, + createBlock, + isUnmodifiedDefaultBlock, +} from '@wordpress/blocks'; import { withDispatch, withSelect } from '@wordpress/data'; import { withInstanceId, compose, withSafeTimeout } from '@wordpress/compose'; import { LEFT, RIGHT, UP, DOWN, BACKSPACE, ENTER } from '@wordpress/keycodes'; @@ -340,21 +345,55 @@ export class InserterMenu extends Component { export default compose( withSelect( ( select, { rootClientId } ) => { const { - getChildBlockNames, - } = select( 'core/blocks' ); - const { + getEditedPostAttribute, + getSelectedBlock, + getInserterItems, getBlockName, } = select( 'core/editor' ); + const { + getChildBlockNames, + } = select( 'core/blocks' ); + const rootBlockName = getBlockName( rootClientId ); + return { + selectedBlock: getSelectedBlock(), rootChildBlocks: getChildBlockNames( rootBlockName ), + title: getEditedPostAttribute( 'title' ), + items: getInserterItems( rootClientId ), + rootClientId, + }; + } ), + withDispatch( ( dispatch, ownProps ) => { + const { + __experimentalFetchReusableBlocks: fetchReusableBlocks, + showInsertionPoint, + hideInsertionPoint, + } = dispatch( 'core/editor' ); + + return { + fetchReusableBlocks, + showInsertionPoint, + hideInsertionPoint, + onSelect( item ) { + const { + replaceBlocks, + insertBlock, + } = dispatch( 'core/editor' ); + const { selectedBlock, index, rootClientId } = ownProps; + const { name, initialAttributes } = item; + + const insertedBlock = createBlock( name, initialAttributes ); + if ( selectedBlock && isUnmodifiedDefaultBlock( selectedBlock ) ) { + replaceBlocks( selectedBlock.clientId, insertedBlock ); + } else { + insertBlock( insertedBlock, index, rootClientId ); + } + + ownProps.onSelect(); + }, }; } ), - withDispatch( ( dispatch ) => ( { - fetchReusableBlocks: dispatch( 'core/editor' ).__experimentalFetchReusableBlocks, - showInsertionPoint: dispatch( 'core/editor' ).showInsertionPoint, - hideInsertionPoint: dispatch( 'core/editor' ).hideInsertionPoint, - } ) ), withSpokenMessages, withInstanceId, withSafeTimeout From 3add7c19e5a922a153a14721810ff681c986409c Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Tue, 30 Oct 2018 13:50:17 -0400 Subject: [PATCH 04/21] Block List: Use default Inserter for sibling insertion (#11018) --- docs/data/data-core-editor.md | 10 +- .../editor/src/components/block-list/block.js | 8 +- .../components/block-list/insertion-point.js | 118 ++++++++---------- .../src/components/block-list/style.scss | 4 +- .../editor/src/components/inserter/index.js | 5 +- .../editor/src/components/inserter/menu.js | 6 +- packages/editor/src/store/actions.js | 10 +- packages/editor/src/store/reducer.js | 14 ++- packages/editor/src/store/selectors.js | 15 ++- packages/editor/src/store/test/reducer.js | 29 +++-- packages/editor/src/store/test/selectors.js | 57 ++++++++- test/e2e/specs/adding-blocks.test.js | 15 ++- 12 files changed, 177 insertions(+), 114 deletions(-) diff --git a/docs/data/data-core-editor.md b/docs/data/data-core-editor.md index 4ce76d80ea62a8..78c2371a6e3e21 100644 --- a/docs/data/data-core-editor.md +++ b/docs/data/data-core-editor.md @@ -707,7 +707,7 @@ otherwise. *Returns* -Whether block is first in mult-selection. +Whether block is first in multi-selection. ### isBlockMultiSelected @@ -1534,7 +1534,7 @@ be inserted, optionally at a specific index respective a root block list. * blocks: Block objects to insert. * index: Index at which block should be inserted. - * rootClientId: Optional root cliente ID of block list on + * rootClientId: Optional root client ID of block list on which to insert. ### showInsertionPoint @@ -1542,6 +1542,12 @@ be inserted, optionally at a specific index respective a root block list. Returns an action object used in signalling that the insertion point should be shown. +*Parameters* + + * rootClientId: Optional root client ID of block list on + which to insert. + * index: Index at which block should be inserted. + ### hideInsertionPoint Returns an action object hiding the insertion point. diff --git a/packages/editor/src/components/block-list/block.js b/packages/editor/src/components/block-list/block.js index 1213d1c424453e..5858f8731fb7ae 100644 --- a/packages/editor/src/components/block-list/block.js +++ b/packages/editor/src/components/block-list/block.js @@ -412,10 +412,9 @@ export class BlockListBlock extends Component { const shouldShowMobileToolbar = shouldAppearSelected; const { error, dragging } = this.state; - // Insertion point can only be made visible when the side inserter is - // not present, and either the block is at the extent of a selection or - // is the first block in the top-level list rendering. - const shouldShowInsertionPoint = ( isPartOfMultiSelection && isFirst ) || ! isPartOfMultiSelection; + // Insertion point can only be made visible if the block is at the + // the extent of a multi-selection, or not in a multi-selection. + const shouldShowInsertionPoint = ( isPartOfMultiSelection && isFirstMultiSelected ) || ! isPartOfMultiSelection; const canShowInBetweenInserter = ! isEmptyDefaultBlock && ! isPreviousBlockADefaultEmptyBlock; // The wp-block className is important for editor styles. @@ -501,7 +500,6 @@ export class BlockListBlock extends Component { clientId={ clientId } rootClientId={ rootClientId } canShowInserter={ canShowInBetweenInserter } - onInsert={ this.hideHoverEffects } /> ) } - { showInsertionPoint &&
} - { showInserter && ( -
- + ) } + { canShowInserter && ( +
+
) } @@ -74,44 +77,23 @@ class BlockInsertionPoint extends Component { ); } } -export default compose( - withSelect( ( select, { clientId, rootClientId, canShowInserter } ) => { - const { - canInsertBlockType, - getBlockIndex, - getBlockInsertionPoint, - getBlock, - isBlockInsertionPointVisible, - isTyping, - } = select( 'core/editor' ); - const { - getDefaultBlockName, - } = select( 'core/blocks' ); - const blockIndex = clientId ? getBlockIndex( clientId, rootClientId ) : -1; - const insertIndex = blockIndex; - const insertionPoint = getBlockInsertionPoint(); - const block = clientId ? getBlock( clientId ) : null; - const showInsertionPoint = ( - isBlockInsertionPointVisible() && - insertionPoint.index === insertIndex && - insertionPoint.rootClientId === rootClientId && - ( ! block || ! isUnmodifiedDefaultBlock( block ) ) - ); +export default withSelect( ( select, { clientId, rootClientId } ) => { + const { + getBlockIndex, + getBlockInsertionPoint, + getBlock, + isBlockInsertionPointVisible, + } = select( 'core/editor' ); + const blockIndex = getBlockIndex( clientId, rootClientId ); + const insertIndex = blockIndex; + const insertionPoint = getBlockInsertionPoint(); + const block = getBlock( clientId ); + const showInsertionPoint = ( + isBlockInsertionPointVisible() && + insertionPoint.index === insertIndex && + insertionPoint.rootClientId === rootClientId && + ! isUnmodifiedDefaultBlock( block ) + ); - const defaultBlockName = getDefaultBlockName(); - return { - canInsertDefaultBlock: canInsertBlockType( defaultBlockName, rootClientId ), - showInserter: ! isTyping() && canShowInserter, - index: insertIndex, - showInsertionPoint, - }; - } ), - ifCondition( ( { canInsertDefaultBlock } ) => canInsertDefaultBlock ), - withDispatch( ( dispatch ) => { - const { insertDefaultBlock, startTyping } = dispatch( 'core/editor' ); - return { - insertDefaultBlock, - startTyping, - }; - } ) -)( BlockInsertionPoint ); + return { showInsertionPoint, insertIndex }; +} )( BlockInsertionPoint ); diff --git a/packages/editor/src/components/block-list/style.scss b/packages/editor/src/components/block-list/style.scss index 5ae32d9bdc7edf..67f1aaa00a61d1 100644 --- a/packages/editor/src/components/block-list/style.scss +++ b/packages/editor/src/components/block-list/style.scss @@ -640,7 +640,7 @@ justify-content: center; // Show a clickable plus. - .editor-block-list__insertion-point-button { + .editor-inserter__toggle { margin-top: -4px; border-radius: 50%; color: $blue-medium-focus; @@ -666,7 +666,7 @@ // Don't show the sibling inserter before the selected block. .edit-post-layout:not(.has-fixed-toolbar) { // The child selector is necessary for this to work properly in nested contexts. - .is-selected > .editor-block-list__insertion-point > .editor-block-list__insertion-point-inserter { + .is-selected > .editor-block-list__insertion-point .editor-inserter__toggle { opacity: 0; pointer-events: none; diff --git a/packages/editor/src/components/inserter/index.js b/packages/editor/src/components/inserter/index.js index 1879683b3c072a..8017fa4b65c1a2 100644 --- a/packages/editor/src/components/inserter/index.js +++ b/packages/editor/src/components/inserter/index.js @@ -99,15 +99,14 @@ class Inserter extends Component { } export default compose( [ - withSelect( ( select, { rootClientId } ) => { + withSelect( ( select, { rootClientId, index } ) => { const { getEditedPostAttribute, getBlockInsertionPoint, getInserterItems, } = select( 'core/editor' ); - let index; - if ( rootClientId === undefined ) { + if ( rootClientId === undefined && index === undefined ) { // Unless explicitly provided, the default insertion point provided // by the store occurs immediately following the selected block. // Otherwise, the default behavior for an undefined index is to diff --git a/packages/editor/src/components/inserter/menu.js b/packages/editor/src/components/inserter/menu.js index 9780661f6442f9..be7623a6a50aaf 100644 --- a/packages/editor/src/components/inserter/menu.js +++ b/packages/editor/src/components/inserter/menu.js @@ -130,10 +130,12 @@ export class InserterMenu extends Component { hoveredItem: item, } ); + const { showInsertionPoint, hideInsertionPoint } = this.props; if ( item ) { - this.props.showInsertionPoint(); + const { rootClientId, index } = this.props; + showInsertionPoint( rootClientId, index ); } else { - this.props.hideInsertionPoint(); + hideInsertionPoint(); } } diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index 2935b833270f0e..d0946c4f0e3d0b 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -312,7 +312,7 @@ export function insertBlock( block, index, rootClientId ) { * * @param {Object[]} blocks Block objects to insert. * @param {?number} index Index at which block should be inserted. - * @param {?string} rootClientId Optional root cliente ID of block list on + * @param {?string} rootClientId Optional root client ID of block list on * which to insert. * * @return {Object} Action object. @@ -331,11 +331,17 @@ export function insertBlocks( blocks, index, rootClientId ) { * Returns an action object used in signalling that the insertion point should * be shown. * + * @param {?string} rootClientId Optional root client ID of block list on + * which to insert. + * @param {?number} index Index at which block should be inserted. + * * @return {Object} Action object. */ -export function showInsertionPoint() { +export function showInsertionPoint( rootClientId, index ) { return { type: 'SHOW_INSERTION_POINT', + rootClientId, + index, }; } diff --git a/packages/editor/src/store/reducer.js b/packages/editor/src/store/reducer.js index 306451865409eb..fc6a1e387d9315 100644 --- a/packages/editor/src/store/reducer.js +++ b/packages/editor/src/store/reducer.js @@ -710,21 +710,23 @@ export function blocksMode( state = {}, action ) { } /** - * Reducer returning the block insertion point visibility, a boolean value - * reflecting whether the insertion point should be shown. + * Reducer returning the block insertion point visibility, either null if there + * is not an explicit insertion point assigned, or an object of its `index` and + * `rootClientId`. * * @param {Object} state Current state. * @param {Object} action Dispatched action. * * @return {Object} Updated state. */ -export function isInsertionPointVisible( state = false, action ) { +export function insertionPoint( state = null, action ) { switch ( action.type ) { case 'SHOW_INSERTION_POINT': - return true; + const { rootClientId, index } = action; + return { rootClientId, index }; case 'HIDE_INSERTION_POINT': - return false; + return null; } return state; @@ -1100,7 +1102,7 @@ export default optimist( combineReducers( { blockSelection, blocksMode, blockListSettings, - isInsertionPointVisible, + insertionPoint, preferences, saving, postLock, diff --git a/packages/editor/src/store/selectors.js b/packages/editor/src/store/selectors.js index ed734f7e5dcbfd..79cd4ca0fc8a05 100644 --- a/packages/editor/src/store/selectors.js +++ b/packages/editor/src/store/selectors.js @@ -1016,10 +1016,10 @@ export function getLastMultiSelectedBlockClientId( state ) { * specified client ID is the first block of the multi-selection set, or false * otherwise. * - * @param {Object} state Editor state. - * @param {string} clientId Block client ID. + * @param {Object} state Editor state. + * @param {string} clientId Block client ID. * - * @return {boolean} Whether block is first in mult-selection. + * @return {boolean} Whether block is first in multi-selection. */ export function isFirstMultiSelectedBlock( state, clientId ) { return getFirstMultiSelectedBlockClientId( state ) === clientId; @@ -1277,7 +1277,12 @@ export function isCaretWithinFormattedText( state ) { export function getBlockInsertionPoint( state ) { let rootClientId, index; - const { end } = state.blockSelection; + const { insertionPoint, blockSelection } = state; + if ( insertionPoint !== null ) { + return insertionPoint; + } + + const { end } = blockSelection; if ( end ) { rootClientId = getBlockRootClientId( state, end ) || undefined; index = getBlockIndex( state, end, rootClientId ) + 1; @@ -1296,7 +1301,7 @@ export function getBlockInsertionPoint( state ) { * @return {?boolean} Whether the insertion point is visible or not. */ export function isBlockInsertionPointVisible( state ) { - return state.isInsertionPointVisible; + return state.insertionPoint !== null; } /** diff --git a/packages/editor/src/store/test/reducer.js b/packages/editor/src/store/test/reducer.js index c0462df96f4112..8ab789e987077a 100644 --- a/packages/editor/src/store/test/reducer.js +++ b/packages/editor/src/store/test/reducer.js @@ -30,7 +30,7 @@ import { preferences, saving, blocksMode, - isInsertionPointVisible, + insertionPoint, reusableBlocks, template, blockListSettings, @@ -1274,27 +1274,36 @@ describe( 'state', () => { } ); } ); - describe( 'isInsertionPointVisible', () => { - it( 'should default to false', () => { - const state = isInsertionPointVisible( undefined, {} ); + describe( 'insertionPoint', () => { + it( 'should default to null', () => { + const state = insertionPoint( undefined, {} ); - expect( state ).toBe( false ); + expect( state ).toBe( null ); } ); - it( 'should set insertion point visible', () => { - const state = isInsertionPointVisible( false, { + it( 'should set insertion point', () => { + const state = insertionPoint( null, { type: 'SHOW_INSERTION_POINT', + rootClientId: 'clientId1', + index: 0, } ); - expect( state ).toBe( true ); + expect( state ).toEqual( { + rootClientId: 'clientId1', + index: 0, + } ); } ); it( 'should clear the insertion point', () => { - const state = isInsertionPointVisible( true, { + const original = deepFreeze( { + rootClientId: 'clientId1', + index: 0, + } ); + const state = insertionPoint( original, { type: 'HIDE_INSERTION_POINT', } ); - expect( state ).toBe( false ); + expect( state ).toBe( null ); } ); } ); diff --git a/packages/editor/src/store/test/selectors.js b/packages/editor/src/store/test/selectors.js index dcfb2016d369ba..157bdb62a5926a 100644 --- a/packages/editor/src/store/test/selectors.js +++ b/packages/editor/src/store/test/selectors.js @@ -2761,6 +2761,40 @@ describe( 'selectors', () => { } ); describe( 'getBlockInsertionPoint', () => { + it( 'should return the explicitly assigned insertion point', () => { + const state = { + currentPost: {}, + preferences: { mode: 'visual' }, + blockSelection: { + start: 'clientId2', + end: 'clientId2', + }, + editor: { + present: { + blocksByClientId: { + clientId1: { clientId: 'clientId1' }, + clientId2: { clientId: 'clientId2' }, + }, + blockOrder: { + '': [ 'clientId1' ], + clientId1: [ 'clientId2' ], + clientId2: [], + }, + edits: {}, + }, + }, + insertionPoint: { + rootClientId: undefined, + index: 0, + }, + }; + + expect( getBlockInsertionPoint( state ) ).toEqual( { + rootClientId: undefined, + index: 0, + } ); + } ); + it( 'should return an object for the selected block', () => { const state = { currentPost: {}, @@ -2781,7 +2815,7 @@ describe( 'selectors', () => { edits: {}, }, }, - isInsertionPointVisible: false, + insertionPoint: null, }; expect( getBlockInsertionPoint( state ) ).toEqual( { @@ -2812,7 +2846,7 @@ describe( 'selectors', () => { edits: {}, }, }, - isInsertionPointVisible: false, + insertionPoint: null, }; expect( getBlockInsertionPoint( state ) ).toEqual( { @@ -2843,7 +2877,7 @@ describe( 'selectors', () => { edits: {}, }, }, - isInsertionPointVisible: false, + insertionPoint: null, }; expect( getBlockInsertionPoint( state ) ).toEqual( { @@ -2874,7 +2908,7 @@ describe( 'selectors', () => { edits: {}, }, }, - isInsertionPointVisible: false, + insertionPoint: null, }; expect( getBlockInsertionPoint( state ) ).toEqual( { @@ -2885,9 +2919,20 @@ describe( 'selectors', () => { } ); describe( 'isBlockInsertionPointVisible', () => { - it( 'should return the value in state', () => { + it( 'should return false if no assigned insertion point', () => { + const state = { + insertionPoint: null, + }; + + expect( isBlockInsertionPointVisible( state ) ).toBe( false ); + } ); + + it( 'should return true if assigned insertion point', () => { const state = { - isInsertionPointVisible: true, + insertionPoint: { + rootClientId: undefined, + index: 5, + }, }; expect( isBlockInsertionPointVisible( state ) ).toBe( true ); diff --git a/test/e2e/specs/adding-blocks.test.js b/test/e2e/specs/adding-blocks.test.js index d4eaf27ad8e45d..0c38f03fdbfa13 100644 --- a/test/e2e/specs/adding-blocks.test.js +++ b/test/e2e/specs/adding-blocks.test.js @@ -67,11 +67,20 @@ describe( 'adding blocks', () => { await page.click( '.editor-post-title__input' ); // Using the between inserter - const insertionPoint = await page.$( '[data-type="core/quote"] .editor-block-list__insertion-point-button' ); + const insertionPoint = await page.$( '[data-type="core/quote"] .editor-inserter__toggle' ); const rect = await insertionPoint.boundingBox(); await page.mouse.move( rect.x + ( rect.width / 2 ), rect.y + ( rect.height / 2 ), { steps: 10 } ); - await page.waitForSelector( '[data-type="core/quote"] .editor-block-list__insertion-point-button' ); - await page.click( '[data-type="core/quote"] .editor-block-list__insertion-point-button' ); + await page.waitForSelector( '[data-type="core/quote"] .editor-inserter__toggle' ); + await page.click( '[data-type="core/quote"] .editor-inserter__toggle' ); + // [TODO]: Search input should be focused immediately. It shouldn't be + // necessary to have `waitForFunction`. + await page.waitForFunction( () => ( + document.activeElement && + document.activeElement.classList.contains( 'editor-inserter__search' ) + ) ); + await page.keyboard.type( 'para' ); + await pressTimes( 'Tab', 3 ); + await page.keyboard.press( 'Enter' ); await page.keyboard.type( 'Second paragraph' ); // Switch to Text Mode to check HTML Output From 9d46788bd90b3fa3697daa0dcad79a9324f327c4 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 30 Oct 2018 19:22:26 +0100 Subject: [PATCH 05/21] Revert using Icon in IconButton to avoid regression in plugin icons (pinned icons) (#11256) --- packages/components/src/icon-button/index.js | 6 +-- .../components/src/icon-button/test/index.js | 2 +- .../test/__snapshots__/index.js.snap | 45 +++++++++---------- 3 files changed, 24 insertions(+), 29 deletions(-) diff --git a/packages/components/src/icon-button/index.js b/packages/components/src/icon-button/index.js index eec8ecd913a51f..4cc8ac426ab4ee 100644 --- a/packages/components/src/icon-button/index.js +++ b/packages/components/src/icon-button/index.js @@ -2,7 +2,7 @@ * External dependencies */ import classnames from 'classnames'; -import { isArray } from 'lodash'; +import { isArray, isString } from 'lodash'; /** * WordPress dependencies @@ -14,7 +14,7 @@ import { Component } from '@wordpress/element'; */ import Tooltip from '../tooltip'; import Button from '../button'; -import Icon from '../icon'; +import Dashicon from '../dashicon'; // This is intentionally a Component class, not a function component because it // is common to apply a ref to the button element (only supported in class) @@ -42,7 +42,7 @@ class IconButton extends Component { let element = ( ); diff --git a/packages/components/src/icon-button/test/index.js b/packages/components/src/icon-button/test/index.js index b93f46a3c5bf66..d5b67eec7930f6 100644 --- a/packages/components/src/icon-button/test/index.js +++ b/packages/components/src/icon-button/test/index.js @@ -20,7 +20,7 @@ describe( 'IconButton', () => { it( 'should render a Dashicon component matching the wordpress icon', () => { const iconButton = shallow( ); - expect( iconButton.find( 'Icon' ).prop( 'icon' ) ).toBe( 'wordpress' ); + expect( iconButton.find( 'Dashicon' ).shallow().hasClass( 'dashicons-wordpress' ) ).toBe( true ); } ); it( 'should render child elements when passed as children', () => { diff --git a/packages/edit-post/src/components/header/more-menu/test/__snapshots__/index.js.snap b/packages/edit-post/src/components/header/more-menu/test/__snapshots__/index.js.snap index 425ee6a645971d..244a185376638c 100644 --- a/packages/edit-post/src/components/header/more-menu/test/__snapshots__/index.js.snap +++ b/packages/edit-post/src/components/header/more-menu/test/__snapshots__/index.js.snap @@ -42,16 +42,22 @@ exports[`MoreMenu should match snapshot 1`] = ` onMouseLeave={[Function]} type="button" > - - - - - - - + /> + + + + From 0f1130048daf87bfed12c3cbf5c1c754211b29f7 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Tue, 30 Oct 2018 15:25:59 -0400 Subject: [PATCH 06/21] Data: Use turbo-combine-reducers in place of Redux (#11255) * Data: Use turbo-combine-reducers in place of Redux * Data: Update package-lock.json for turbo-combine-reducers --- package-lock.json | 8 +++++++- packages/data/CHANGELOG.md | 10 ++++++++-- packages/data/package.json | 3 ++- packages/data/src/index.js | 2 +- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index ed71b4aeeb4313..5d5dcb1b6c26b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2189,7 +2189,8 @@ "equivalent-key-map": "^0.2.2", "is-promise": "^2.1.0", "lodash": "^4.17.10", - "redux": "^4.0.0" + "redux": "^4.0.0", + "turbo-combine-reducers": "^1.0.2" } }, "@wordpress/date": { @@ -19933,6 +19934,11 @@ "safe-buffer": "^5.0.1" } }, + "turbo-combine-reducers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/turbo-combine-reducers/-/turbo-combine-reducers-1.0.2.tgz", + "integrity": "sha512-gHbdMZlA6Ym6Ur5pSH/UWrNQMIM9IqTH6SoL1DbHpqEdQ8i+cFunSmSlFykPt0eGQwZ4d/XTHOl74H0/kFBVWw==" + }, "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", diff --git a/packages/data/CHANGELOG.md b/packages/data/CHANGELOG.md index bc8f8aee7bd42b..ce5e89b914db2f 100644 --- a/packages/data/CHANGELOG.md +++ b/packages/data/CHANGELOG.md @@ -1,3 +1,9 @@ +## 3.0.1 (Unreleased) + +### Internal + +- Replace Redux implementation of `combineReducers` with in-place-compatible `turbo-combine-reducers`. + ## 3.0.0 (2018-10-29) ### Breaking Changes @@ -10,7 +16,7 @@ ## 2.1.0 (2018-09-30) -## New Features +### New Features - Adding support for using controls in resolvers using the controls plugin. @@ -22,7 +28,7 @@ - Writing resolvers as async generators has been deprecated. Use the controls plugin instead. -## Bug Fixes +### Bug Fixes - Fix the promise middleware in Firefox. diff --git a/packages/data/package.json b/packages/data/package.json index 471486a547eb64..abe657181c52a8 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -29,7 +29,8 @@ "equivalent-key-map": "^0.2.2", "is-promise": "^2.1.0", "lodash": "^4.17.10", - "redux": "^4.0.0" + "redux": "^4.0.0", + "turbo-combine-reducers": "^1.0.2" }, "devDependencies": { "deep-freeze": "^0.0.1", diff --git a/packages/data/src/index.js b/packages/data/src/index.js index 2e71774abcdbbf..122f9c261b760b 100644 --- a/packages/data/src/index.js +++ b/packages/data/src/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { combineReducers } from 'redux'; +import combineReducers from 'turbo-combine-reducers'; /** * Internal dependencies From 39d5e21df83b0e59b62db6a9bceb47fae94a401f Mon Sep 17 00:00:00 2001 From: Miguel Fonseca Date: Tue, 30 Oct 2018 19:27:01 +0000 Subject: [PATCH 07/21] Update plugin version to 4.2.0. (#11258) --- 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 1d123094d09330..09ed86af7aa590 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.1 + * Version: 4.2.0-rc.1 * Author: Gutenberg Team * * @package gutenberg diff --git a/package-lock.json b/package-lock.json index 5d5dcb1b6c26b6..e34e319d22a7fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "4.1.1", + "version": "4.2.0-rc.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index e55538173a0f0e..5ecc9d58868ec5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "4.1.1", + "version": "4.2.0-rc.1", "private": true, "description": "A new WordPress editor experience", "repository": "git+https://github.com/WordPress/gutenberg.git", From 160cc0c7009de548af2cf26aee56f8fef9df202f Mon Sep 17 00:00:00 2001 From: Grzegorz Ziolkowski Date: Tue, 30 Oct 2018 21:10:52 +0100 Subject: [PATCH 08/21] chore(release): publish - @wordpress/api-fetch@2.2.1 - @wordpress/block-library@2.1.7 - @wordpress/blocks@5.1.1 - @wordpress/components@5.0.1 - @wordpress/core-data@2.0.8 - @wordpress/data@3.0.1 - @wordpress/edit-post@2.0.2 - @wordpress/editor@6.1.0 - @wordpress/format-library@1.0.2 - @wordpress/keycodes@2.0.3 - @wordpress/list-reusable-blocks@1.1.6 - @wordpress/notices@1.0.1 - @wordpress/nux@2.0.8 - @wordpress/rich-text@2.0.1 - @wordpress/viewport@2.0.7 --- packages/api-fetch/package.json | 2 +- packages/block-library/package.json | 2 +- packages/blocks/package.json | 2 +- packages/components/package.json | 2 +- packages/core-data/package.json | 2 +- packages/data/package.json | 2 +- packages/edit-post/package.json | 2 +- packages/editor/package.json | 2 +- packages/format-library/package.json | 2 +- packages/keycodes/package.json | 2 +- packages/list-reusable-blocks/package.json | 2 +- packages/notices/package.json | 2 +- packages/nux/package.json | 2 +- packages/rich-text/package.json | 2 +- packages/viewport/package.json | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/api-fetch/package.json b/packages/api-fetch/package.json index 5a540c1cb2a623..2fcc7ce88b236a 100644 --- a/packages/api-fetch/package.json +++ b/packages/api-fetch/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/api-fetch", - "version": "2.2.0", + "version": "2.2.1", "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 99378cfd77f3fb..98f546fb9f186d 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-library", - "version": "2.1.6", + "version": "2.1.7", "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 395a73310bada4..348287e08367c4 100644 --- a/packages/blocks/package.json +++ b/packages/blocks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/blocks", - "version": "5.1.0", + "version": "5.1.1", "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 8c4df8e4dadb6c..d01a75826df0e5 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/components", - "version": "5.0.0", + "version": "5.0.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 6ce9acdcdbb540..aa48096df8c46b 100644 --- a/packages/core-data/package.json +++ b/packages/core-data/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/core-data", - "version": "2.0.7", + "version": "2.0.8", "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 abe657181c52a8..596cad5d3a3c84 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/data", - "version": "3.0.0", + "version": "3.0.1", "description": "Data 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 3ae06ca6baa103..09b0d0ecbeec6c 100644 --- a/packages/edit-post/package.json +++ b/packages/edit-post/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-post", - "version": "2.0.1", + "version": "2.0.2", "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 2d2253cd3d3607..3ac12f6921fa48 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/editor", - "version": "6.0.1", + "version": "6.1.0", "description": "Building blocks for WordPress editors.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/format-library/package.json b/packages/format-library/package.json index 2bbf6563d7de38..d1db967c54b29c 100644 --- a/packages/format-library/package.json +++ b/packages/format-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/format-library", - "version": "1.0.1", + "version": "1.0.2", "description": "Format library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/keycodes/package.json b/packages/keycodes/package.json index 602472cd5025a1..331f0f40a03233 100644 --- a/packages/keycodes/package.json +++ b/packages/keycodes/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/keycodes", - "version": "2.0.2", + "version": "2.0.3", "description": "Keycodes utilities for WordPress. Used to check for keyboard events across browsers/operating systems.", "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 026457330f64f5..68822f07f2db41 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.5", + "version": "1.1.6", "description": "Adding Export/Import support to the reusable blocks listing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/notices/package.json b/packages/notices/package.json index 93e9ac42d9900e..27a27545c0c9f7 100644 --- a/packages/notices/package.json +++ b/packages/notices/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/notices", - "version": "1.0.0", + "version": "1.0.1", "description": "State management for notices.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/nux/package.json b/packages/nux/package.json index 46a2cf510f3f62..4f5cbaa9acff12 100644 --- a/packages/nux/package.json +++ b/packages/nux/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/nux", - "version": "2.0.7", + "version": "2.0.8", "description": "NUX (New User eXperience) module for WordPress.", "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 6c2474383df7da..36a06be61f1d31 100644 --- a/packages/rich-text/package.json +++ b/packages/rich-text/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/rich-text", - "version": "2.0.0", + "version": "2.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 3fcab4650d3bfb..c7eb6ef08dc1ab 100644 --- a/packages/viewport/package.json +++ b/packages/viewport/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/viewport", - "version": "2.0.6", + "version": "2.0.7", "description": "Viewport module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", From 3600c88fda01b62463769b5e8c3e795b064e12c1 Mon Sep 17 00:00:00 2001 From: Grzegorz Ziolkowski Date: Tue, 30 Oct 2018 21:18:50 +0100 Subject: [PATCH 09/21] chore(release): update changelog files --- packages/api-fetch/CHANGELOG.md | 4 +++- packages/block-library/CHANGELOG.md | 2 ++ packages/blocks/CHANGELOG.md | 2 ++ packages/components/CHANGELOG.md | 2 ++ packages/core-data/CHANGELOG.md | 2 ++ packages/data/CHANGELOG.md | 2 +- packages/edit-post/CHANGELOG.md | 2 ++ packages/editor/CHANGELOG.md | 2 +- packages/format-library/CHANGELOG.md | 2 ++ packages/keycodes/CHANGELOG.md | 2 ++ packages/list-reusable-blocks/CHANGELOG.md | 2 ++ packages/notices/CHANGELOG.md | 2 ++ packages/rich-text/CHANGELOG.md | 2 ++ packages/viewport/CHANGELOG.md | 2 ++ 14 files changed, 27 insertions(+), 3 deletions(-) diff --git a/packages/api-fetch/CHANGELOG.md b/packages/api-fetch/CHANGELOG.md index a10c2b53851316..a59aee8634fc73 100644 --- a/packages/api-fetch/CHANGELOG.md +++ b/packages/api-fetch/CHANGELOG.md @@ -1,4 +1,6 @@ -# 2.2.0 (2018-10-29) +## 2.2.1 (2018-10-30) + +## 2.2.0 (2018-10-29) ### New Feature diff --git a/packages/block-library/CHANGELOG.md b/packages/block-library/CHANGELOG.md index f3a7e80e7e4651..8df197bb991957 100644 --- a/packages/block-library/CHANGELOG.md +++ b/packages/block-library/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.1.7 (2018-10-30) + ## 2.1.6 (2018-10-30) ### Bug Fixes diff --git a/packages/blocks/CHANGELOG.md b/packages/blocks/CHANGELOG.md index 7db53884fe79e3..dd08eb8aa02756 100644 --- a/packages/blocks/CHANGELOG.md +++ b/packages/blocks/CHANGELOG.md @@ -1,3 +1,5 @@ +## 5.1.1 (2018-10-30) + ## 5.1.0 (2018-10-30) ### New feature diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 7351a93a587db3..d0c7e552baf1dd 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -1,3 +1,5 @@ +## 5.0.1 (2018-10-30) + ## 5.0.0 (2018-10-29) ### Breaking Change diff --git a/packages/core-data/CHANGELOG.md b/packages/core-data/CHANGELOG.md index efb7fe6c22c47d..f8286ef8c09abb 100644 --- a/packages/core-data/CHANGELOG.md +++ b/packages/core-data/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.0.8 (2018-10-30) + ## 2.0.6 (2018-10-22) ## 2.0.5 (2018-10-19) diff --git a/packages/data/CHANGELOG.md b/packages/data/CHANGELOG.md index ce5e89b914db2f..34ed8c5a73bb3c 100644 --- a/packages/data/CHANGELOG.md +++ b/packages/data/CHANGELOG.md @@ -1,4 +1,4 @@ -## 3.0.1 (Unreleased) +## 3.0.1 (2018-10-30) ### Internal diff --git a/packages/edit-post/CHANGELOG.md b/packages/edit-post/CHANGELOG.md index 105e2b38412604..bb14a1d78bb856 100644 --- a/packages/edit-post/CHANGELOG.md +++ b/packages/edit-post/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.0.2 (2018-10-30) + ## 2.0.1 (2018-10-30) ## 2.0.0 (2018-10-29) diff --git a/packages/editor/CHANGELOG.md b/packages/editor/CHANGELOG.md index 8ef9c5cce31eaf..3b0ed28447c317 100644 --- a/packages/editor/CHANGELOG.md +++ b/packages/editor/CHANGELOG.md @@ -1,4 +1,4 @@ -## 6.1.0 (Unreleased) +## 6.1.0 (2018-10-30) ### Deprecations diff --git a/packages/format-library/CHANGELOG.md b/packages/format-library/CHANGELOG.md index ab080e1495e9b6..60840015bcccfc 100644 --- a/packages/format-library/CHANGELOG.md +++ b/packages/format-library/CHANGELOG.md @@ -1,3 +1,5 @@ +## 1.0.2 (2018-10-30) + ## 1.0.1 (2018-10-30) ## 1.0.0 (2018-10-29) diff --git a/packages/keycodes/CHANGELOG.md b/packages/keycodes/CHANGELOG.md index a49cefcd0d75cf..9c0e0dcaa72dce 100644 --- a/packages/keycodes/CHANGELOG.md +++ b/packages/keycodes/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.0.3 (2018-10-30) + ## 2.0.0 (2018-09-05) ### Breaking Change diff --git a/packages/list-reusable-blocks/CHANGELOG.md b/packages/list-reusable-blocks/CHANGELOG.md index 6234aa478b9abb..0b07680b95a9aa 100644 --- a/packages/list-reusable-blocks/CHANGELOG.md +++ b/packages/list-reusable-blocks/CHANGELOG.md @@ -1,3 +1,5 @@ +## 1.1.6 (2018-10-30) + ## 1.1.4 (2018-10-22) ## 1.1.3 (2018-10-19) diff --git a/packages/notices/CHANGELOG.md b/packages/notices/CHANGELOG.md index 69606f9c9fc37d..7e9aad2e81b11a 100644 --- a/packages/notices/CHANGELOG.md +++ b/packages/notices/CHANGELOG.md @@ -1,3 +1,5 @@ +## 1.0.1 (2018-10-30) + ## 1.0.0 (2018-10-29) - Initial release. diff --git a/packages/rich-text/CHANGELOG.md b/packages/rich-text/CHANGELOG.md index 3efab18e80d667..07a1cf023dd80e 100644 --- a/packages/rich-text/CHANGELOG.md +++ b/packages/rich-text/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.0.1 (2018-10-30) + ## 2.0.0 (2018-10-30) - Remove `@wordpress/blocks` as a dependency. diff --git a/packages/viewport/CHANGELOG.md b/packages/viewport/CHANGELOG.md index 9b4bad0bd4aa56..7e7b548c9ef068 100644 --- a/packages/viewport/CHANGELOG.md +++ b/packages/viewport/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.0.7 (2018-10-30) + ## 2.0.5 (2018-10-19) ## 2.0.4 (2018-10-18) From 3d9e99bbf81a1270b85f176e0f761a8ae0bdbad5 Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Wed, 31 Oct 2018 11:20:00 +1100 Subject: [PATCH 10/21] Rename parentClientId to rootClientId for consistency (#11274) We refer to the same concept as rootClientId elsewhere in the application. --- docs/data/data-core-editor.md | 12 ++---- .../src/components/autocompleters/block.js | 8 ++-- .../src/components/inner-blocks/index.js | 4 +- packages/editor/src/store/selectors.js | 42 +++++++++---------- 4 files changed, 29 insertions(+), 37 deletions(-) diff --git a/docs/data/data-core-editor.md b/docs/data/data-core-editor.md index 78c2371a6e3e21..ebbc303649e17e 100644 --- a/docs/data/data-core-editor.md +++ b/docs/data/data-core-editor.md @@ -1074,17 +1074,13 @@ Post content. ### canInsertBlockType -Determines if the given block type is allowed to be inserted, and, if -parentClientId is provided, whether it is allowed to be nested within the -given parent. +Determines if the given block type is allowed to be inserted into the block list. *Parameters* * state: Editor state. - * blockName: The name of the given block type, e.g. - 'core/paragraph'. - * parentClientId: The parent that the given block is to be - nested within, or null. + * blockName: The name of the block type, e.g.' core/paragraph'. + * rootClientId: Optional root client ID of block list. *Returns* @@ -1114,7 +1110,7 @@ Items are returned ordered descendingly by their 'utility' and 'frecency'. *Parameters* * state: Editor state. - * parentClientId: The block we are inserting into, if any. + * rootClientId: Optional root client ID of block list. *Returns* diff --git a/packages/editor/src/components/autocompleters/block.js b/packages/editor/src/components/autocompleters/block.js index c864e2a77edb69..e48bf2f48e7380 100644 --- a/packages/editor/src/components/autocompleters/block.js +++ b/packages/editor/src/components/autocompleters/block.js @@ -23,14 +23,14 @@ function defaultGetBlockInsertionParentClientId() { /** * Returns the inserter items for the specified parent block. * - * @param {string} parentClientId Client ID of the block for which to retrieve - * inserter items. + * @param {string} rootClientId Client ID of the block for which to retrieve + * inserter items. * * @return {Array} The inserter items for the specified * parent. */ -function defaultGetInserterItems( parentClientId ) { - return select( 'core/editor' ).getInserterItems( parentClientId ); +function defaultGetInserterItems( rootClientId ) { + return select( 'core/editor' ).getInserterItems( rootClientId ); } /** diff --git a/packages/editor/src/components/inner-blocks/index.js b/packages/editor/src/components/inner-blocks/index.js index 0c06acd345eb5f..c9252e912b5df1 100644 --- a/packages/editor/src/components/inner-blocks/index.js +++ b/packages/editor/src/components/inner-blocks/index.js @@ -128,12 +128,12 @@ InnerBlocks = compose( [ getTemplateLock, } = select( 'core/editor' ); const { clientId } = ownProps; - const parentClientId = getBlockRootClientId( clientId ); + const rootClientId = getBlockRootClientId( clientId ); return { isSelectedBlockInRoot: isBlockSelected( clientId ) || hasSelectedInnerBlock( clientId ), block: getBlock( clientId ), blockListSettings: getBlockListSettings( clientId ), - parentLock: getTemplateLock( parentClientId ), + parentLock: getTemplateLock( rootClientId ), }; } ), withDispatch( ( dispatch, ownProps ) => { diff --git a/packages/editor/src/store/selectors.js b/packages/editor/src/store/selectors.js index 79cd4ca0fc8a05..b31ff36d46794f 100644 --- a/packages/editor/src/store/selectors.js +++ b/packages/editor/src/store/selectors.js @@ -1506,20 +1506,16 @@ export const getEditedPostContent = createSelector( ); /** - * Determines if the given block type is allowed to be inserted, and, if - * parentClientId is provided, whether it is allowed to be nested within the - * given parent. + * Determines if the given block type is allowed to be inserted into the block list. * - * @param {Object} state Editor state. - * @param {string} blockName The name of the given block type, e.g. - * 'core/paragraph'. - * @param {?string} parentClientId The parent that the given block is to be - * nested within, or null. + * @param {Object} state Editor state. + * @param {string} blockName The name of the block type, e.g.' core/paragraph'. + * @param {?string} rootClientId Optional root client ID of block list. * * @return {boolean} Whether the given block type is allowed to be inserted. */ export const canInsertBlockType = createSelector( - ( state, blockName, parentClientId = null ) => { + ( state, blockName, rootClientId = null ) => { const checkAllowList = ( list, item, defaultResult = null ) => { if ( isBoolean( list ) ) { return list; @@ -1542,17 +1538,17 @@ export const canInsertBlockType = createSelector( return false; } - const isLocked = !! getTemplateLock( state, parentClientId ); + const isLocked = !! getTemplateLock( state, rootClientId ); if ( isLocked ) { return false; } - const parentBlockListSettings = getBlockListSettings( state, parentClientId ); + const parentBlockListSettings = getBlockListSettings( state, rootClientId ); const parentAllowedBlocks = get( parentBlockListSettings, [ 'allowedBlocks' ] ); const hasParentAllowedBlock = checkAllowList( parentAllowedBlocks, blockName ); const blockAllowedParentBlocks = blockType.parent; - const parentName = getBlockName( state, parentClientId ); + const parentName = getBlockName( state, rootClientId ); const hasBlockAllowedParent = checkAllowList( blockAllowedParentBlocks, parentName ); if ( hasParentAllowedBlock !== null && hasBlockAllowedParent !== null ) { @@ -1565,9 +1561,9 @@ export const canInsertBlockType = createSelector( return true; }, - ( state, blockName, parentClientId ) => [ - state.blockListSettings[ parentClientId ], - state.editor.present.blocksByClientId[ parentClientId ], + ( state, blockName, rootClientId ) => [ + state.blockListSettings[ rootClientId ], + state.editor.present.blocksByClientId[ rootClientId ], state.settings.allowedBlockTypes, state.settings.templateLock, ], @@ -1607,8 +1603,8 @@ function getInsertUsage( state, id ) { * * Items are returned ordered descendingly by their 'utility' and 'frecency'. * - * @param {Object} state Editor state. - * @param {?string} parentClientId The block we are inserting into, if any. + * @param {Object} state Editor state. + * @param {?string} rootClientId Optional root client ID of block list. * * @return {Editor.InserterItem[]} Items that appear in inserter. * @@ -1626,7 +1622,7 @@ function getInsertUsage( state, id ) { * @property {number} frecency Hueristic that combines frequency and recency. */ export const getInserterItems = createSelector( - ( state, parentClientId = null ) => { + ( state, rootClientId = null ) => { const calculateUtility = ( category, count, isContextual ) => { if ( isContextual ) { return INSERTER_UTILITY_HIGH; @@ -1664,7 +1660,7 @@ export const getInserterItems = createSelector( return false; } - return canInsertBlockType( state, blockType.name, parentClientId ); + return canInsertBlockType( state, blockType.name, rootClientId ); }; const buildBlockTypeInserterItem = ( blockType ) => { @@ -1694,7 +1690,7 @@ export const getInserterItems = createSelector( }; const shouldIncludeReusableBlock = ( reusableBlock ) => { - if ( ! canInsertBlockType( state, 'core/block', parentClientId ) ) { + if ( ! canInsertBlockType( state, 'core/block', rootClientId ) ) { return false; } @@ -1708,7 +1704,7 @@ export const getInserterItems = createSelector( return false; } - if ( ! canInsertBlockType( state, referencedBlockType.name, parentClientId ) ) { + if ( ! canInsertBlockType( state, referencedBlockType.name, rootClientId ) ) { return false; } @@ -1753,8 +1749,8 @@ export const getInserterItems = createSelector( [ 'desc', 'desc' ] ); }, - ( state, parentClientId ) => [ - state.blockListSettings[ parentClientId ], + ( state, rootClientId ) => [ + state.blockListSettings[ rootClientId ], state.editor.present.blockOrder, state.editor.present.blocksByClientId, state.preferences.insertUsage, From 55d5aac9550ca423caeba8359ddc17a48e28f21b Mon Sep 17 00:00:00 2001 From: Marko Andrijasevic Date: Wed, 31 Oct 2018 08:01:29 +0100 Subject: [PATCH 11/21] Nux package: fix incorrect named deprecated import (#11283) DotTip component was attempting to import a named export of deprecated which is not provided by @wordpress/deprecated package. This switches it to use the deafult export instead. --- packages/nux/src/components/dot-tip/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nux/src/components/dot-tip/index.js b/packages/nux/src/components/dot-tip/index.js index dbd81c45d13a62..45c7a768e2089b 100644 --- a/packages/nux/src/components/dot-tip/index.js +++ b/packages/nux/src/components/dot-tip/index.js @@ -5,7 +5,7 @@ import { compose } from '@wordpress/compose'; import { Popover, Button, IconButton } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { withSelect, withDispatch } from '@wordpress/data'; -import { deprecated } from '@wordpress/deprecated'; +import deprecated from '@wordpress/deprecated'; function getAnchorRect( anchor ) { // The default getAnchorRect() excludes an element's top and bottom padding From 8f5c78c2af94fac569d5c69cc1c6feb0d8f66388 Mon Sep 17 00:00:00 2001 From: Eric Murphy Date: Wed, 31 Oct 2018 17:26:28 +0700 Subject: [PATCH 12/21] Fixed "artifact" misspelling in docs. (#11291) --- docs/language.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/language.md b/docs/language.md index 5a88f901bb81ba..8a71e707619fbc 100644 --- a/docs/language.md +++ b/docs/language.md @@ -22,7 +22,7 @@ Additionally, how do we even know this came from our editor? Maybe someone snuck A Gutenberg post is the proper block-aware representation of a post, a collection of semantically consistent descriptions of what each block is and what its essential data is. This representation only ever exists in memory. It is the [chase](https://en.wikipedia.org/wiki/Chase_(printing)) in the typesetter's workshop, ever-shifting as sorts are attached and repositioned. -A Gutenberg post is not the artefact it produces, namely the `post_content`. The latter is the printed page, optimized for the reader, but retaining its invisible markings for later editing. +A Gutenberg post is not the artifact it produces, namely the `post_content`. The latter is the printed page, optimized for the reader, but retaining its invisible markings for later editing. Later sections of this document will refer to _Gutenberg post_ and to _blocks_. These are to be assumed to not be the `post_content` or the invisible markings. From 529e3a2d081bef9c096226e6ce9302f9354194d7 Mon Sep 17 00:00:00 2001 From: Dominik Schilling Date: Wed, 31 Oct 2018 11:49:17 +0100 Subject: [PATCH 13/21] Increase specificity for active radio/checkbox input styling (#11290) --- packages/edit-post/src/style.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/edit-post/src/style.scss b/packages/edit-post/src/style.scss index fbc5dbfd755a4c..23c040743210c9 100644 --- a/packages/edit-post/src/style.scss +++ b/packages/edit-post/src/style.scss @@ -237,7 +237,7 @@ body.block-editor-page { input[type="checkbox"] { border-radius: $radius-round-rectangle / 2; - &::before { + &:checked::before { margin: -4px 0 0 -5px; color: $white; } @@ -246,7 +246,7 @@ body.block-editor-page { input[type="radio"] { border-radius: $radius-round; - &::before { + &:checked::before { margin: 3px 0 0 3px; background-color: $white; } From 73d9759116dde896931f4d152f186147a57889fe Mon Sep 17 00:00:00 2001 From: Timothy Jacobs Date: Wed, 31 Oct 2018 07:34:56 -0400 Subject: [PATCH 14/21] Add complete post type labels for Resuable Blocks (#11278) * Add complete post type labels for Resuable Blocks Fixes #11272. * Clean up PHPCS issues --- lib/register.php | 53 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/lib/register.php b/lib/register.php index 0401af2f8a7c75..b86df4fd7a37f0 100644 --- a/lib/register.php +++ b/lib/register.php @@ -444,9 +444,27 @@ function gutenberg_register_post_types() { 'wp_block', array( 'labels' => array( - 'name' => __( 'Blocks', 'gutenberg' ), - 'singular_name' => __( 'Block', 'gutenberg' ), - 'search_items' => __( 'Search Blocks', 'gutenberg' ), + 'name' => _x( 'Blocks', 'post type general name', 'gutenberg' ), + 'singular_name' => _x( 'Block', 'post type singular name', 'gutenberg' ), + 'menu_name' => _x( 'Blocks', 'admin menu', 'gutenberg' ), + 'name_admin_bar' => _x( 'Block', 'add new on admin bar', 'gutenberg' ), + 'add_new' => _x( 'Add New', 'Block', 'gutenberg' ), + 'add_new_item' => __( 'Add New Block', 'gutenberg' ), + 'new_item' => __( 'New Block', 'gutenberg' ), + 'edit_item' => __( 'Edit Block', 'gutenberg' ), + 'view_item' => __( 'View Block', 'gutenberg' ), + 'all_items' => __( 'All Blocks', 'gutenberg' ), + 'search_items' => __( 'Search Blocks', 'gutenberg' ), + 'not_found' => __( 'No blocks found.', 'gutenberg' ), + 'not_found_in_trash' => __( 'No blocks found in Trash.', 'gutenberg' ), + 'filter_items_list' => __( 'Filter blocks list', 'gutenberg' ), + 'items_list_navigation' => __( 'Blocks list navigation', 'gutenberg' ), + 'items_list' => __( 'Blocks list', 'gutenberg' ), + 'item_published' => __( 'Block published.', 'gutenberg' ), + 'item_published_privately' => __( 'Block published privately.', 'gutenberg' ), + 'item_reverted_to_draft' => __( 'Block reverted to draft.', 'gutenberg' ), + 'item_scheduled' => __( 'Block scheduled.', 'gutenberg' ), + 'item_updated' => __( 'Block updated.', 'gutenberg' ), ), 'public' => false, 'show_ui' => true, @@ -516,6 +534,35 @@ function gutenberg_register_post_types() { } add_action( 'init', 'gutenberg_register_post_types' ); +/** + * Apply the correct labels for Reusable Blocks in the bulk action updated messages. + * + * @since 4.3.0 + * + * @param array $messages Arrays of messages, each keyed by the corresponding post type. + * @param array $bulk_counts Array of item counts for each message, used to build internationalized strings. + * + * @return array + */ +function gutenberg_bulk_post_updated_messages( $messages, $bulk_counts ) { + $messages['wp_block'] = array( + // translators: Number of blocks updated. + 'updated' => _n( '%s block updated.', '%s blocks updated.', $bulk_counts['updated'], 'gutenberg' ), + // translators: Blocks not updated because they're locked. + 'locked' => ( 1 == $bulk_counts['locked'] ) ? __( '1 block not updated, somebody is editing it.', 'gutenberg' ) : _n( '%s block not updated, somebody is editing it.', '%s blocks not updated, somebody is editing them.', $bulk_counts['locked'], 'gutenberg' ), + // translators: Number of blocks deleted. + 'deleted' => _n( '%s block permanently deleted.', '%s blocks permanently deleted.', $bulk_counts['deleted'], 'gutenberg' ), + // translators: Number of blocks trashed. + 'trashed' => _n( '%s block moved to the Trash.', '%s blocks moved to the Trash.', $bulk_counts['trashed'], 'gutenberg' ), + // translators: Number of blocks untrashed. + 'untrashed' => _n( '%s block restored from the Trash.', '%s blocks restored from the Trash.', $bulk_counts['untrashed'], 'gutenberg' ), + ); + + return $messages; +} + +add_filter( 'bulk_post_updated_messages', 'gutenberg_bulk_post_updated_messages', 10, 2 ); + /** * Injects a hidden input in the edit form to propagate the information that classic editor is selected. * From a5ee143b965ff46d23d66d4782cf71b79575e46f Mon Sep 17 00:00:00 2001 From: Luke Pettway Date: Wed, 31 Oct 2018 08:02:38 -0400 Subject: [PATCH 15/21] added myself to the contributors list (#11260) --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 2b7c128f77cdcf..0b1cd656f6bdd9 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -111,3 +111,4 @@ This list is manually curated to include valuable contributions by volunteers th | @ajitbohra | | | @ChrisVanPatten | | | @tofumatt | @lonelyvegan | +| @LukePettway | @luke_pettway | \ No newline at end of file From 58725c42980f81dbcf18d921be7ca43507c968e0 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Wed, 31 Oct 2018 08:40:35 -0400 Subject: [PATCH 16/21] Components: Remove redundant onClickOutside handler from Dropdown (#11253) --- packages/components/src/dropdown/index.js | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/packages/components/src/dropdown/index.js b/packages/components/src/dropdown/index.js index c8ba61f4ad1837..c99c0c3e8ea47f 100644 --- a/packages/components/src/dropdown/index.js +++ b/packages/components/src/dropdown/index.js @@ -11,12 +11,13 @@ import Popover from '../popover'; class Dropdown extends Component { constructor() { super( ...arguments ); + this.toggle = this.toggle.bind( this ); this.close = this.close.bind( this ); - this.clickOutside = this.clickOutside.bind( this ); - this.bindContainer = this.bindContainer.bind( this ); this.refresh = this.refresh.bind( this ); + this.popoverRef = createRef(); + this.state = { isOpen: false, }; @@ -38,10 +39,6 @@ class Dropdown extends Component { } } - bindContainer( ref ) { - this.container = ref; - } - /** * When contents change height due to user interaction, * `refresh` can be called to re-render Popover with correct @@ -59,12 +56,6 @@ class Dropdown extends Component { } ) ); } - clickOutside( event ) { - if ( ! this.container.contains( event.target ) ) { - this.close(); - } - } - close() { this.setState( { isOpen: false } ); } @@ -84,7 +75,7 @@ class Dropdown extends Component { const args = { isOpen, onToggle: this.toggle, onClose: this.close }; return ( -
+
{ renderToggle( args ) } { isOpen && ( From 8ad5fbd555a25afb103d14d0f2a4e19885429e38 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Wed, 31 Oct 2018 13:45:01 +0100 Subject: [PATCH 17/21] Remove findDOMNode from Tooltip component (#11169) * Remove findDOMNode from Tooltip component --- packages/components/CHANGELOG.md | 6 ++ packages/components/src/icon-button/index.js | 7 ++- packages/components/src/tooltip/index.js | 62 -------------------- 3 files changed, 11 insertions(+), 64 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index d0c7e552baf1dd..fe1414540b9cbd 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -1,3 +1,9 @@ +## 5.0.2 (Unreleased) + +### Polish + +- Tooltip are no longer removed when Button becomes disabled, it's left to the component rendering the Tooltip. + ## 5.0.1 (2018-10-30) ## 5.0.0 (2018-10-29) diff --git a/packages/components/src/icon-button/index.js b/packages/components/src/icon-button/index.js index 4cc8ac426ab4ee..b90ee85d2b6080 100644 --- a/packages/components/src/icon-button/index.js +++ b/packages/components/src/icon-button/index.js @@ -25,7 +25,7 @@ class IconButton extends Component { const tooltipText = tooltip || label; // Should show the tooltip if... - const showTooltip = ( + const showTooltip = ! additionalProps.disabled && ( // an explicit tooltip is passed or... tooltip || // there's a shortcut or... @@ -49,7 +49,10 @@ class IconButton extends Component { if ( showTooltip ) { element = ( - + { element } ); diff --git a/packages/components/src/tooltip/index.js b/packages/components/src/tooltip/index.js index de177eeb7492b1..3bb1f6c8b7ccb7 100644 --- a/packages/components/src/tooltip/index.js +++ b/packages/components/src/tooltip/index.js @@ -10,7 +10,6 @@ import { Component, Children, cloneElement, - findDOMNode, concatChildren, } from '@wordpress/element'; @@ -31,7 +30,6 @@ class Tooltip extends Component { constructor() { super( ...arguments ); - this.bindNode = this.bindNode.bind( this ); this.delayedSetIsOver = debounce( ( isOver ) => this.setState( { isOver } ), TOOLTIP_DELAY @@ -44,65 +42,6 @@ class Tooltip extends Component { componentWillUnmount() { this.delayedSetIsOver.cancel(); - this.disconnectDisabledAttributeObserver(); - } - - componentDidUpdate( prevProps, prevState ) { - const { isOver } = this.state; - if ( isOver !== prevState.isOver ) { - if ( isOver ) { - this.observeDisabledAttribute(); - } else { - this.disconnectDisabledAttributeObserver(); - } - } - } - - /** - * Assigns DOM node of the rendered component as an instance property. - * - * @param {Element} ref Rendered component reference. - */ - bindNode( ref ) { - // Disable reason: Because render clones the child, we don't know what - // type of element we have, but if it's a DOM node, we want to observe - // the disabled attribute. - // eslint-disable-next-line react/no-find-dom-node - this.node = findDOMNode( ref ); - } - - /** - * Disconnects any DOM observer attached to the rendered node. - */ - disconnectDisabledAttributeObserver() { - if ( this.observer ) { - this.observer.disconnect(); - } - } - - /** - * Adds a DOM observer to the rendered node, if supported and if the DOM - * node exists, to monitor for application of a disabled attribute. - */ - observeDisabledAttribute() { - if ( ! window.MutationObserver || ! this.node ) { - return; - } - - this.observer = new window.MutationObserver( ( [ mutation ] ) => { - if ( mutation.target.disabled ) { - // We can assume here that isOver is true, because mutation - // observer is only attached for duration of isOver active - this.setState( { isOver: false } ); - } - } ); - - // Monitor changes to the disable attribute on the DOM node - this.observer.observe( this.node, { - subtree: true, - attributes: true, - attributeFilter: [ 'disabled' ], - } ); } emitToChild( eventName, event ) { @@ -163,7 +102,6 @@ class Tooltip extends Component { const child = Children.only( children ); const { isOver } = this.state; return cloneElement( child, { - ref: this.bindNode, onMouseEnter: this.createToggleIsOver( 'onMouseEnter', true ), onMouseLeave: this.createToggleIsOver( 'onMouseLeave' ), onClick: this.createToggleIsOver( 'onClick' ), From 0001042c24179572cc9b32883a97c8fa03d32953 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Wed, 31 Oct 2018 08:45:45 -0400 Subject: [PATCH 18/21] RichText: Remove unused `ref` assignment to RichText (#11222) --- packages/editor/src/components/rich-text/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/editor/src/components/rich-text/index.js b/packages/editor/src/components/rich-text/index.js index 2548240a6671f9..6b5decb63331dd 100644 --- a/packages/editor/src/components/rich-text/index.js +++ b/packages/editor/src/components/rich-text/index.js @@ -15,7 +15,7 @@ import memize from 'memize'; /** * WordPress dependencies */ -import { Component, Fragment, RawHTML, createRef } from '@wordpress/element'; +import { Component, Fragment, RawHTML } from '@wordpress/element'; import { isHorizontalEdge, getRectangleFromRange, @@ -115,7 +115,6 @@ export class RichText extends Component { this.formatToValue = memize( this.formatToValue.bind( this ), { size: 1 } ); this.savedContent = value; - this.containerRef = createRef(); this.patterns = getPatterns( { onReplace, multilineTag: this.multilineTag, @@ -933,7 +932,6 @@ export class RichText extends Component { return (
{ isSelected && ! inlineToolbar && ( From 7b69168af5bb98c72f87e80016e35d3a48587c83 Mon Sep 17 00:00:00 2001 From: Danilo Ercoli Date: Wed, 31 Oct 2018 14:04:05 +0100 Subject: [PATCH 19/21] Export `switchToBlockType` to be used mobile side when merging two blocks. (#11294) --- packages/blocks/src/api/index.native.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/blocks/src/api/index.native.js b/packages/blocks/src/api/index.native.js index c83db0ad39e2c5..e987acbdaaf4e4 100644 --- a/packages/blocks/src/api/index.native.js +++ b/packages/blocks/src/api/index.native.js @@ -1,5 +1,6 @@ export { createBlock, + switchToBlockType, } from './factory'; export { default as parse, From ab5d5ceb61bc808cd92aadac0585ba76d3041fb9 Mon Sep 17 00:00:00 2001 From: Tim Wright Date: Wed, 31 Oct 2018 09:06:27 -0400 Subject: [PATCH 20/21] Fixed typos on block api documentation (#11298) * merge from upstream * fixed typos in block API documentation --- docs/block-api.md | 4 ++-- gutenberg-mobile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/block-api.md b/docs/block-api.md index 769ff8105680c4..a3df73ffcb80e6 100644 --- a/docs/block-api.md +++ b/docs/block-api.md @@ -140,7 +140,7 @@ styles: [ ], ``` -Plugins and Themes can also register [custom block style](../docs/extensibility/extending-blocks/#block-style-variations) for exisiting blocks. +Plugins and Themes can also register [custom block style](../docs/extensibility/extending-blocks/#block-style-variations) for existing blocks. #### Attributes (optional) @@ -490,7 +490,7 @@ className: false, html: false, ``` -- `inserter` (default `true`): By default, all blocks will appear in the Gutenberg inserter. To hide a block so that it can only be inserted programatically, set `inserter` to `false`. +- `inserter` (default `true`): By default, all blocks will appear in the Gutenberg inserter. To hide a block so that it can only be inserted programmatically, set `inserter` to `false`. ```js // Hide this block from the inserter. diff --git a/gutenberg-mobile b/gutenberg-mobile index c90c36410d4c0c..776bb23f7f19ef 160000 --- a/gutenberg-mobile +++ b/gutenberg-mobile @@ -1 +1 @@ -Subproject commit c90c36410d4c0c3f2dea84ca666b1b585408a03f +Subproject commit 776bb23f7f19ef57b491f9708e9795b3a2ddb68e From 3503c0d08a18ad2036fc0329be82dcbbd8e03e4e Mon Sep 17 00:00:00 2001 From: William Earnhardt Date: Wed, 31 Oct 2018 09:38:11 -0400 Subject: [PATCH 21/21] Fix property path on get() call (#10962) --- .../src/components/post-taxonomies/flat-term-selector.js | 4 ++-- .../components/post-taxonomies/hierarchical-term-selector.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/editor/src/components/post-taxonomies/flat-term-selector.js b/packages/editor/src/components/post-taxonomies/flat-term-selector.js index 55e2f6e9c47c84..7e2a322d042847 100644 --- a/packages/editor/src/components/post-taxonomies/flat-term-selector.js +++ b/packages/editor/src/components/post-taxonomies/flat-term-selector.js @@ -174,12 +174,12 @@ class FlatTermSelector extends Component { const termNames = availableTerms.map( ( term ) => term.name ); const newTermLabel = get( taxonomy, - [ 'data', 'labels', 'add_new_item' ], + [ 'labels', 'add_new_item' ], slug === 'post_tag' ? __( 'Add New Tag' ) : __( 'Add New Term' ) ); const singularName = get( taxonomy, - [ 'data', 'labels', 'singular_name' ], + [ 'labels', 'singular_name' ], slug === 'post_tag' ? __( 'Tag' ) : __( 'Term' ) ); const termAddedLabel = sprintf( _x( '%s added', 'term' ), singularName ); diff --git a/packages/editor/src/components/post-taxonomies/hierarchical-term-selector.js b/packages/editor/src/components/post-taxonomies/hierarchical-term-selector.js index 482d65cf62abb8..7d3e60d17dcf29 100644 --- a/packages/editor/src/components/post-taxonomies/hierarchical-term-selector.js +++ b/packages/editor/src/components/post-taxonomies/hierarchical-term-selector.js @@ -247,7 +247,7 @@ class HierarchicalTermSelector extends Component { const { filteredTermsTree, formName, formParent, isRequestingTerms, showForm, filterValue } = this.state; const labelWithFallback = ( labelProperty, fallbackIsCategory, fallbackIsNotCategory ) => get( taxonomy, - [ 'data', 'labels', labelProperty ], + [ 'labels', labelProperty ], slug === 'category' ? fallbackIsCategory : fallbackIsNotCategory ); const newTermButtonLabel = labelWithFallback(