+ );
}
export default compose( [
diff --git a/packages/block-editor/src/components/rich-text/index.js b/packages/block-editor/src/components/rich-text/index.js
index 006ec4a697760b..c6cfad310c89c6 100644
--- a/packages/block-editor/src/components/rich-text/index.js
+++ b/packages/block-editor/src/components/rich-text/index.js
@@ -42,6 +42,8 @@ import {
isCollapsed,
LINE_SEPARATOR,
indentListItems,
+ __unstableGetActiveFormats,
+ __unstableUpdateFormats,
} from '@wordpress/rich-text';
import { decodeEntities } from '@wordpress/html-entities';
import { withFilters, IsolatedEventContainer } from '@wordpress/components';
@@ -82,6 +84,13 @@ const INSERTION_INPUT_TYPES_TO_IGNORE = new Set( [
'insertLink',
] );
+/**
+ * Global stylesheet.
+ */
+const globalStyle = document.createElement( 'style' );
+
+document.head.appendChild( globalStyle );
+
export class RichText extends Component {
constructor( { value, onReplace, multiline } ) {
super( ...arguments );
@@ -126,7 +135,10 @@ export class RichText extends Component {
this.handleHorizontalNavigation = this.handleHorizontalNavigation.bind( this );
this.onPointerDown = this.onPointerDown.bind( this );
- this.formatToValue = memize( this.formatToValue.bind( this ), { size: 1 } );
+ this.formatToValue = memize(
+ this.formatToValue.bind( this ),
+ { maxSize: 1 }
+ );
this.savedContent = value;
this.patterns = getPatterns( {
@@ -176,9 +188,9 @@ export class RichText extends Component {
*/
getRecord() {
const { formats, replacements, text } = this.formatToValue( this.props.value );
- const { start, end, selectedFormat } = this.state;
+ const { start, end, activeFormats } = this.state;
- return { formats, replacements, text, start, end, selectedFormat };
+ return { formats, replacements, text, start, end, activeFormats };
}
createRecord() {
@@ -355,6 +367,8 @@ export class RichText extends Component {
unstableOnFocus();
}
+ this.recalculateBoundaryStyle();
+
document.addEventListener( 'selectionchange', this.onSelectionChange );
}
@@ -393,40 +407,19 @@ export class RichText extends Component {
}
}
- let { selectedFormat } = this.state;
- const { formats, replacements, text, start, end } = this.createRecord();
-
- if ( this.formatPlaceholder ) {
- selectedFormat = this.formatPlaceholder.length;
-
- if ( selectedFormat > 0 ) {
- formats[ this.state.start ] = this.formatPlaceholder;
- } else {
- delete formats[ this.state.start ];
- }
- } else if ( selectedFormat > 0 ) {
- const formatsBefore = formats[ start - 1 ] || [];
- const formatsAfter = formats[ start ] || [];
-
- let source = formatsBefore;
-
- if ( formatsAfter.length > formatsBefore.length ) {
- source = formatsAfter;
- }
-
- source = source.slice( 0, selectedFormat );
-
- formats[ this.state.start ] = source;
- } else {
- delete formats[ this.state.start ];
- }
-
- const change = { formats, replacements, text, start, end, selectedFormat };
+ const value = this.createRecord();
+ const { activeFormats = [], start } = this.state;
- this.onChange( change, {
- withoutHistory: true,
+ // Update the formats between the last and new caret position.
+ const change = __unstableUpdateFormats( {
+ value,
+ start,
+ end: value.start,
+ formats: activeFormats,
} );
+ this.onChange( change, { withoutHistory: true } );
+
const transformed = this.patterns.reduce(
( accumlator, transform ) => transform( accumlator ),
change
@@ -434,7 +427,7 @@ export class RichText extends Component {
if ( transformed !== change ) {
this.onCreateUndoLevel();
- this.onChange( { ...transformed, selectedFormat } );
+ this.onChange( { ...transformed, activeFormats } );
}
// Create an undo level when input stops for over a second.
@@ -454,36 +447,40 @@ export class RichText extends Component {
* Handles the `selectionchange` event: sync the selection to local state.
*/
onSelectionChange() {
- if ( this.ignoreSelectionChange ) {
- delete this.ignoreSelectionChange;
- return;
- }
-
const value = this.createRecord();
- const { start, end, formats } = value;
+ const { start, end } = value;
if ( start !== this.state.start || end !== this.state.end ) {
- const isCaretWithinFormattedText = this.props.isCaretWithinFormattedText;
+ const { isCaretWithinFormattedText } = this.props;
+ const activeFormats = __unstableGetActiveFormats( value );
- if ( ! isCaretWithinFormattedText && formats[ start ] ) {
+ if ( ! isCaretWithinFormattedText && activeFormats.length ) {
this.props.onEnterFormattedText();
- } else if ( isCaretWithinFormattedText && ! formats[ start ] ) {
+ } else if ( isCaretWithinFormattedText && ! activeFormats.length ) {
this.props.onExitFormattedText();
}
- let selectedFormat;
-
- if ( isCollapsed( value ) ) {
- const formatsBefore = formats[ start - 1 ] || [];
- const formatsAfter = formats[ start ] || [];
+ this.setState( { start, end, activeFormats } );
+ this.applyRecord( { ...value, activeFormats }, { domOnly: true } );
- selectedFormat = Math.min( formatsBefore.length, formatsAfter.length );
+ if ( activeFormats.length > 0 ) {
+ this.recalculateBoundaryStyle();
}
+ }
+ }
+
+ recalculateBoundaryStyle() {
+ const boundarySelector = '*[data-rich-text-format-boundary]';
+ const element = this.editableRef.querySelector( boundarySelector );
- this.setState( { start, end, selectedFormat } );
- this.applyRecord( { ...value, selectedFormat }, { domOnly: true } );
+ if ( element ) {
+ const computedStyle = getComputedStyle( element );
+ const newColor = computedStyle.color
+ .replace( ')', ', 0.2)' )
+ .replace( 'rgb', 'rgba' );
- delete this.formatPlaceholder;
+ globalStyle.innerHTML =
+ `*:focus ${ boundarySelector }{background-color: ${ newColor }}`;
}
}
@@ -511,14 +508,12 @@ export class RichText extends Component {
onChange( record, { withoutHistory } = {} ) {
this.applyRecord( record );
- const { start, end, formatPlaceholder, selectedFormat } = record;
+ const { start, end, activeFormats = [] } = record;
- this.formatPlaceholder = formatPlaceholder;
this.onChangeEditableValue( record );
-
this.savedContent = this.valueToFormat( record );
this.props.onChange( this.savedContent );
- this.setState( { start, end, selectedFormat } );
+ this.setState( { start, end, activeFormats } );
if ( ! withoutHistory ) {
this.onCreateUndoLevel();
@@ -734,17 +729,15 @@ export class RichText extends Component {
handleHorizontalNavigation( event ) {
const value = this.createRecord();
const { formats, text, start, end } = value;
- const { selectedFormat } = this.state;
+ const { activeFormats = [] } = this.state;
const collapsed = isCollapsed( value );
const isReverse = event.keyCode === LEFT;
- delete this.formatPlaceholder;
-
// If the selection is collapsed and at the very start, do nothing if
// navigating backward.
// If the selection is collapsed and at the very end, do nothing if
// navigating forward.
- if ( collapsed && selectedFormat === 0 ) {
+ if ( collapsed && activeFormats.length === 0 ) {
if ( start === 0 && isReverse ) {
return;
}
@@ -764,38 +757,43 @@ export class RichText extends Component {
// In all other cases, prevent default behaviour.
event.preventDefault();
- // Ignore the selection change handler when setting selection, all state
- // will be set here.
- this.ignoreSelectionChange = true;
-
const formatsBefore = formats[ start - 1 ] || [];
const formatsAfter = formats[ start ] || [];
- let newSelectedFormat = selectedFormat;
+ let newActiveFormatsLength = activeFormats.length;
+ let source = formatsAfter;
+
+ if ( formatsBefore.length > formatsAfter.length ) {
+ source = formatsBefore;
+ }
// If the amount of formats before the caret and after the caret is
// different, the caret is at a format boundary.
if ( formatsBefore.length < formatsAfter.length ) {
- if ( ! isReverse && selectedFormat < formatsAfter.length ) {
- newSelectedFormat++;
+ if ( ! isReverse && activeFormats.length < formatsAfter.length ) {
+ newActiveFormatsLength++;
}
- if ( isReverse && selectedFormat > formatsBefore.length ) {
- newSelectedFormat--;
+ if ( isReverse && activeFormats.length > formatsBefore.length ) {
+ newActiveFormatsLength--;
}
} else if ( formatsBefore.length > formatsAfter.length ) {
- if ( ! isReverse && selectedFormat > formatsAfter.length ) {
- newSelectedFormat--;
+ if ( ! isReverse && activeFormats.length > formatsAfter.length ) {
+ newActiveFormatsLength--;
}
- if ( isReverse && selectedFormat < formatsBefore.length ) {
- newSelectedFormat++;
+ if ( isReverse && activeFormats.length < formatsBefore.length ) {
+ newActiveFormatsLength++;
}
}
- if ( newSelectedFormat !== selectedFormat ) {
- this.applyRecord( { ...value, selectedFormat: newSelectedFormat } );
- this.setState( { selectedFormat: newSelectedFormat } );
+ // Wait for boundary class to be added.
+ setTimeout( () => this.recalculateBoundaryStyle() );
+
+ if ( newActiveFormatsLength !== activeFormats.length ) {
+ const newActiveFormats = source.slice( 0, newActiveFormatsLength );
+ this.applyRecord( { ...value, activeFormats: newActiveFormats } );
+ this.setState( { activeFormats: newActiveFormats } );
return;
}
@@ -806,7 +804,7 @@ export class RichText extends Component {
...value,
start: newPos,
end: newPos,
- selectedFormat: isReverse ? formatsBefore.length : formatsAfter.length,
+ activeFormats: isReverse ? formatsBefore : formatsAfter,
} );
}
diff --git a/packages/block-editor/src/components/rich-text/style.scss b/packages/block-editor/src/components/rich-text/style.scss
index 16fdc282020e0c..4a52ddb56fcd37 100644
--- a/packages/block-editor/src/components/rich-text/style.scss
+++ b/packages/block-editor/src/components/rich-text/style.scss
@@ -45,27 +45,6 @@
*[data-rich-text-format-boundary] {
border-radius: 2px;
- box-shadow: 0 0 0 1px $light-gray-400;
- background: $light-gray-400;
-
- // Enforce a dark text color so active inline boundaries
- // are always readable.
- // See https://github.com/WordPress/gutenberg/issues/9508
- color: $dark-gray-900;
- }
-
- // Link inline boundaries get special colors.
- a[data-rich-text-format-boundary] {
- box-shadow: 0 0 0 1px $blue-medium-100;
- background: $blue-medium-100;
- color: $blue-medium-900;
- }
-
- // inline boundaries need special treatment because their
- // un-selected style is already padded.
- code[data-rich-text-format-boundary] {
- background: $light-gray-400;
- box-shadow: 0 0 0 1px $light-gray-400;
}
}
diff --git a/packages/block-library/src/cover/editor.scss b/packages/block-library/src/cover/editor.scss
index c74979dc0969d4..b368b3ee24c05d 100644
--- a/packages/block-library/src/cover/editor.scss
+++ b/packages/block-library/src/cover/editor.scss
@@ -1,10 +1,5 @@
.wp-block-cover-image,
.wp-block-cover {
- .block-editor-rich-text__editable:focus a[data-rich-text-format-boundary] {
- box-shadow: none;
- background: rgba(255, 255, 255, 0.3);
- }
-
&.components-placeholder h2 {
color: inherit;
}
diff --git a/packages/block-library/src/embed/edit.js b/packages/block-library/src/embed/edit.js
index 0e251a8f0aafd0..fcc305d9e8a7c3 100644
--- a/packages/block-library/src/embed/edit.js
+++ b/packages/block-library/src/embed/edit.js
@@ -61,13 +61,24 @@ export function getEmbedEditComponent( title, icon, responsive = true ) {
if ( switchedPreview || switchedURL ) {
if ( this.props.cannotEmbed ) {
- // Can't embed this URL, and we've just received or switched the preview.
+ // We either have a new preview or a new URL, but we can't embed it.
+ if ( ! this.props.fetching ) {
+ // If we're not fetching the preview, then we know it can't be embedded, so try
+ // removing any trailing slash, and resubmit.
+ this.resubmitWithoutTrailingSlash();
+ }
return;
}
this.handleIncomingPreview();
}
}
+ resubmitWithoutTrailingSlash() {
+ this.setState( ( prevState ) => ( {
+ url: prevState.url.replace( /\/$/, '' ),
+ } ), this.setUrl );
+ }
+
setUrl( event ) {
if ( event ) {
event.preventDefault();
diff --git a/packages/block-library/src/embed/util.js b/packages/block-library/src/embed/util.js
index 085ea4b22ff8c8..9cba9f50364745 100644
--- a/packages/block-library/src/embed/util.js
+++ b/packages/block-library/src/embed/util.js
@@ -46,7 +46,7 @@ export const findBlock = ( url ) => {
};
export const isFromWordPress = ( html ) => {
- return includes( html, 'class="wp-embedded-content" data-secret' );
+ return includes( html, 'class="wp-embedded-content"' );
};
export const getPhotoHtml = ( photo ) => {
diff --git a/packages/block-library/src/gallery/editor.scss b/packages/block-library/src/gallery/editor.scss
index 0340f66a31e8e5..34b08797e2e558 100644
--- a/packages/block-library/src/gallery/editor.scss
+++ b/packages/block-library/src/gallery/editor.scss
@@ -82,10 +82,6 @@ ul.wp-block-gallery li {
a {
color: $white;
}
-
- &:focus a[data-rich-text-format-boundary] {
- color: rgba(0, 0, 0, 0.2);
- }
}
}
diff --git a/packages/block-library/src/preformatted/index.js b/packages/block-library/src/preformatted/index.js
index 7d90224f9639cd..9553ac77885f98 100644
--- a/packages/block-library/src/preformatted/index.js
+++ b/packages/block-library/src/preformatted/index.js
@@ -68,10 +68,14 @@ export const settings = {
return (
' ) }
onChange={ ( nextContent ) => {
setAttributes( {
- content: nextContent,
+ // Ensure line breaks are normalised to characters. This
+ // saves space, is easier to read, and ensures display
+ // filters work correctly.
+ content: nextContent.replace( / /g, '\n' ),
} );
} }
placeholder={ __( 'Write preformatted text…' ) }
diff --git a/packages/blocks/src/api/raw-handling/paste-handler.js b/packages/blocks/src/api/raw-handling/paste-handler.js
index b40164f549689b..1d67540c0b9225 100644
--- a/packages/blocks/src/api/raw-handling/paste-handler.js
+++ b/packages/blocks/src/api/raw-handling/paste-handler.js
@@ -127,9 +127,14 @@ export function pasteHandler( { HTML = '', plainText = '', mode = 'AUTO', tagNam
// First of all, strip any meta tags.
HTML = HTML.replace( /]+>/, '' );
- // If we detect block delimiters, parse entirely as blocks.
- if ( mode !== 'INLINE' && HTML.indexOf( '
-
Pre text
Foo
+
Pre text
+
+Foo
diff --git a/packages/e2e-tests/specs/__snapshots__/multi-block-selection.test.js.snap b/packages/e2e-tests/specs/__snapshots__/multi-block-selection.test.js.snap
index 6986ffc94968bc..f68e40f606dc4c 100644
--- a/packages/e2e-tests/specs/__snapshots__/multi-block-selection.test.js.snap
+++ b/packages/e2e-tests/specs/__snapshots__/multi-block-selection.test.js.snap
@@ -6,6 +6,8 @@ exports[`Multi-block selection should allow selecting outer edge if there is no
"
`;
+exports[`Multi-block selection should always expand single line selection 1`] = `""`;
+
exports[`Multi-block selection should only trigger multi-selection when at the end 1`] = `
"
1.
diff --git a/packages/e2e-tests/specs/__snapshots__/rich-text.test.js.snap b/packages/e2e-tests/specs/__snapshots__/rich-text.test.js.snap
index f5c79ddbd33ac5..bf5fefd31a7e32 100644
--- a/packages/e2e-tests/specs/__snapshots__/rich-text.test.js.snap
+++ b/packages/e2e-tests/specs/__snapshots__/rich-text.test.js.snap
@@ -18,6 +18,12 @@ exports[`RichText should apply formatting with primary shortcut 1`] = `
"
`;
+exports[`RichText should apply multiple formats when selection is collapsed 1`] = `
+"
+
1.
+"
+`;
+
exports[`RichText should handle change in tag name gracefully 1`] = `
"
diff --git a/packages/e2e-tests/specs/__snapshots__/writing-flow.test.js.snap b/packages/e2e-tests/specs/__snapshots__/writing-flow.test.js.snap
index 41f6fe04a2b531..2a80f20633a953 100644
--- a/packages/e2e-tests/specs/__snapshots__/writing-flow.test.js.snap
+++ b/packages/e2e-tests/specs/__snapshots__/writing-flow.test.js.snap
@@ -126,6 +126,16 @@ exports[`adding blocks should navigate around nested inline boundaries 2`] = `
"
`;
+exports[`adding blocks should navigate empty paragraph 1`] = `
+"
+
1
+
+
+
+
2
+"
+`;
+
exports[`adding blocks should not create extra line breaks in multiline value 1`] = `
"
diff --git a/packages/e2e-tests/specs/blocks/__snapshots__/preformatted.test.js.snap b/packages/e2e-tests/specs/blocks/__snapshots__/preformatted.test.js.snap
index 4538816e15b411..07a70a19a6b2be 100644
--- a/packages/e2e-tests/specs/blocks/__snapshots__/preformatted.test.js.snap
+++ b/packages/e2e-tests/specs/blocks/__snapshots__/preformatted.test.js.snap
@@ -9,6 +9,8 @@ exports[`Preformatted should preserve character newlines 1`] = `
exports[`Preformatted should preserve character newlines 2`] = `
"
-
0 1 2
+
0
+1
+2
"
`;
diff --git a/packages/e2e-tests/specs/embedding.test.js b/packages/e2e-tests/specs/embedding.test.js
index 3ea51358b14b21..6fb26c02281c70 100644
--- a/packages/e2e-tests/specs/embedding.test.js
+++ b/packages/e2e-tests/specs/embedding.test.js
@@ -9,6 +9,8 @@ import {
createJSONResponse,
getEditedPostContent,
clickButton,
+ insertBlock,
+ publishPost,
} from '@wordpress/e2e-test-utils';
const MOCK_EMBED_WORDPRESS_SUCCESS_RESPONSE = {
@@ -60,6 +62,10 @@ const MOCK_BAD_WORDPRESS_RESPONSE = {
};
const MOCK_RESPONSES = [
+ {
+ match: createEmbeddingMatcher( 'https://wordpress.org/gutenberg/handbook' ),
+ onRequestMatch: createJSONResponse( MOCK_BAD_WORDPRESS_RESPONSE ),
+ },
{
match: createEmbeddingMatcher( 'https://wordpress.org/gutenberg/handbook/' ),
onRequestMatch: createJSONResponse( MOCK_BAD_WORDPRESS_RESPONSE ),
@@ -80,6 +86,10 @@ const MOCK_RESPONSES = [
match: createEmbeddingMatcher( 'https://twitter.com/notnownikki' ),
onRequestMatch: createJSONResponse( MOCK_EMBED_RICH_SUCCESS_RESPONSE ),
},
+ {
+ match: createEmbeddingMatcher( 'https://twitter.com/notnownikki/' ),
+ onRequestMatch: createJSONResponse( MOCK_CANT_EMBED_RESPONSE ),
+ },
{
match: createEmbeddingMatcher( 'https://twitter.com/thatbunty' ),
onRequestMatch: createJSONResponse( MOCK_BAD_EMBED_PROVIDER_RESPONSE ),
@@ -173,6 +183,17 @@ describe( 'Embedding content', () => {
expect( await getEditedPostContent() ).toMatchSnapshot();
} );
+ it( 'should retry embeds that could not be embedded with trailing slashes, without the trailing slashes', async () => {
+ await clickBlockAppender();
+ await page.keyboard.type( '/embed' );
+ await page.keyboard.press( 'Enter' );
+ // This URL can't be embedded, but without the trailing slash, it can.
+ await page.keyboard.type( 'https://twitter.com/notnownikki/' );
+ await page.keyboard.press( 'Enter' );
+ // The twitter block should appear correctly.
+ await page.waitForSelector( 'figure.wp-block-embed-twitter' );
+ } );
+
it( 'should allow the user to try embedding a failed URL again', async () => {
// URL that can't be embedded.
await clickBlockAppender();
@@ -192,4 +213,27 @@ describe( 'Embedding content', () => {
await clickButton( 'Try again' );
await page.waitForSelector( 'figure.wp-block-embed-twitter' );
} );
+
+ it( 'should switch to the WordPress block correctly', async () => {
+ // This test is to make sure that WordPress embeds are detected correctly,
+ // because the HTML can vary, and the block is detected by looking for
+ // classes in the HTML, so we need to flag up if the HTML changes.
+
+ // Publish a post to embed.
+ await insertBlock( 'Paragraph' );
+ await page.keyboard.type( 'Hello there!' );
+ await publishPost();
+ const postUrl = await page.$eval( '#inspector-text-control-0', ( el ) => el.value );
+
+ // Start a new post, embed the previous post.
+ await createNewPost();
+ await clickBlockAppender();
+ await page.keyboard.type( '/embed' );
+ await page.keyboard.press( 'Enter' );
+ await page.keyboard.type( postUrl );
+ await page.keyboard.press( 'Enter' );
+
+ // Check the block has become a WordPress block.
+ await page.waitForSelector( '.wp-block-embed-wordpress' );
+ } );
} );
diff --git a/packages/e2e-tests/specs/multi-block-selection.test.js b/packages/e2e-tests/specs/multi-block-selection.test.js
index 76ea211367660d..6bf7a62bf7789e 100644
--- a/packages/e2e-tests/specs/multi-block-selection.test.js
+++ b/packages/e2e-tests/specs/multi-block-selection.test.js
@@ -181,6 +181,19 @@ describe( 'Multi-block selection', () => {
expect( await getEditedPostContent() ).toMatchSnapshot();
} );
+ it( 'should always expand single line selection', async () => {
+ await clickBlockAppender();
+ await page.keyboard.press( 'Enter' );
+ await page.keyboard.type( '12' );
+ await page.keyboard.press( 'ArrowLeft' );
+ await pressKeyWithModifier( 'shift', 'ArrowRight' );
+ await pressKeyWithModifier( 'shift', 'ArrowUp' );
+ // This delete all blocks.
+ await page.keyboard.press( 'Backspace' );
+
+ expect( await getEditedPostContent() ).toMatchSnapshot();
+ } );
+
it( 'should allow selecting outer edge if there is no sibling block', async () => {
await clickBlockAppender();
await page.keyboard.type( '1' );
diff --git a/packages/e2e-tests/specs/rich-text.test.js b/packages/e2e-tests/specs/rich-text.test.js
index 5153032953899e..dcb1434abd89be 100644
--- a/packages/e2e-tests/specs/rich-text.test.js
+++ b/packages/e2e-tests/specs/rich-text.test.js
@@ -58,6 +58,34 @@ describe( 'RichText', () => {
expect( await getEditedPostContent() ).toMatchSnapshot();
} );
+ it( 'should apply multiple formats when selection is collapsed', async () => {
+ await clickBlockAppender();
+ await pressKeyWithModifier( 'primary', 'b' );
+ await pressKeyWithModifier( 'primary', 'i' );
+ await page.keyboard.type( '1' );
+ await pressKeyWithModifier( 'primary', 'i' );
+ await pressKeyWithModifier( 'primary', 'b' );
+ await page.keyboard.type( '.' );
+
+ expect( await getEditedPostContent() ).toMatchSnapshot();
+ } );
+
+ it( 'should not highlight more than one format', async () => {
+ await clickBlockAppender();
+ await pressKeyWithModifier( 'primary', 'b' );
+ await page.keyboard.type( '1' );
+ await pressKeyWithModifier( 'primary', 'b' );
+ await page.keyboard.type( ' 2' );
+ await pressKeyWithModifier( 'shift', 'ArrowLeft' );
+ await pressKeyWithModifier( 'primary', 'b' );
+
+ const count = await page.evaluate( () => document.querySelectorAll(
+ '*[data-rich-text-format-boundary]'
+ ).length );
+
+ expect( count ).toBe( 1 );
+ } );
+
it( 'should return focus when pressing formatting button', async () => {
await clickBlockAppender();
await page.keyboard.type( 'Some ' );
diff --git a/packages/e2e-tests/specs/writing-flow.test.js b/packages/e2e-tests/specs/writing-flow.test.js
index 62ece158016405..c186507a7cdef3 100644
--- a/packages/e2e-tests/specs/writing-flow.test.js
+++ b/packages/e2e-tests/specs/writing-flow.test.js
@@ -298,4 +298,16 @@ describe( 'adding blocks', () => {
// Check that none of the paragraph blocks have in them.
expect( await getEditedPostContent() ).toMatchSnapshot();
} );
+
+ it( 'should navigate empty paragraph', async () => {
+ await clickBlockAppender();
+ await page.keyboard.press( 'Enter' );
+ await page.waitForFunction( () => document.activeElement.isContentEditable );
+ await page.keyboard.press( 'ArrowLeft' );
+ await page.keyboard.type( '1' );
+ await page.keyboard.press( 'ArrowRight' );
+ await page.keyboard.type( '2' );
+
+ expect( await getEditedPostContent() ).toMatchSnapshot();
+ } );
} );
diff --git a/packages/edit-post/CHANGELOG.md b/packages/edit-post/CHANGELOG.md
index db8c68da542295..4e58838aade5e6 100644
--- a/packages/edit-post/CHANGELOG.md
+++ b/packages/edit-post/CHANGELOG.md
@@ -5,7 +5,9 @@
* Expose the `className` property to style the `PluginSidebar` component.
### Bug Fixes
- - Fix 'save' keyboard shortcut not functioning in the Code Editor.
+
+- Fix 'save' keyboard shortcut not functioning in the Code Editor.
+- Prevent `ClipboardButton` from incorrectly copying a serialized block string instead of the intended text in Safari.
## 3.1.7 (2019-01-03)
diff --git a/packages/edit-post/src/components/block-settings-menu/plugin-block-settings-menu-item.js b/packages/edit-post/src/components/block-settings-menu/plugin-block-settings-menu-item.js
index 810d558346cc79..7a7451a983ce3b 100644
--- a/packages/edit-post/src/components/block-settings-menu/plugin-block-settings-menu-item.js
+++ b/packages/edit-post/src/components/block-settings-menu/plugin-block-settings-menu-item.js
@@ -6,7 +6,7 @@ import { difference } from 'lodash';
/**
* WordPress dependencies
*/
-import { IconButton } from '@wordpress/components';
+import { MenuItem } from '@wordpress/components';
import { compose } from '@wordpress/compose';
/**
@@ -89,7 +89,7 @@ const PluginBlockSettingsMenuItem = ( { allowedBlocks, icon, label, onClick, sma
if ( ! shouldRenderItem( selectedBlocks, allowedBlocks ) ) {
return null;
}
- return (
{ ! small && label }
- );
+ );
} }
);
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 4e0df7f3749b08..b855ca046c3866 100644
--- a/packages/edit-post/src/components/header/feature-toggle/index.js
+++ b/packages/edit-post/src/components/header/feature-toggle/index.js
@@ -26,7 +26,6 @@ function FeatureToggle( { onToggle, isActive, label, info, messageActivated, mes
isSelected={ isActive }
onClick={ flow( onToggle, speakMessage ) }
role="menuitemcheckbox"
- label={ label }
info={ info }
>
{ label }
diff --git a/packages/edit-post/src/components/header/plugin-more-menu-item/test/__snapshots__/index.js.snap b/packages/edit-post/src/components/header/plugin-more-menu-item/test/__snapshots__/index.js.snap
index 6e395b2e7ebae9..7fa0869a60f223 100644
--- a/packages/edit-post/src/components/header/plugin-more-menu-item/test/__snapshots__/index.js.snap
+++ b/packages/edit-post/src/components/header/plugin-more-menu-item/test/__snapshots__/index.js.snap
@@ -18,6 +18,7 @@ exports[`PluginMoreMenuItem renders menu item as button properly 1`] = `
>