Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions packages/editor/src/components/rich-text/format.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,22 @@ export function createBlockChildrenFromTinyMCEElement( element ) {
};
}

export function createHTMLElementFromTinyMCEElement( element ) {
const cleanElement = document.createElement( element.nodeName.toLowerCase() );

for ( let i = 0; i < element.attributes.length; i++ ) {
const { name, value } = element.attributes[ i ];

if ( ! isTinyMCEInternalAttribute( name ) ) {
cleanElement.setAttribute( name, value );
}
}

cleanElement.appendChild( domToDOM( element.childNodes ) );

return cleanElement;
}

/**
* Given an array of HTMLElement from a TinyMCE editor body element, returns an
* equivalent WPBlockChildren value. The element may undergo some preprocessing
Expand Down Expand Up @@ -177,6 +193,36 @@ export function domToString( value ) {
return children.toHTML( domToBlockChildren( value ) );
}

export function domToDOM( value ) {
const fragment = document.createDocumentFragment();

for ( let i = 0; i < value.length; i++ ) {
let node = value[ i ];
switch ( node.nodeType ) {
case TEXT_NODE:
node = document.createTextNode( getCleanTextNodeValue( node ) );
if ( node.length ) {
fragment.appendChild( node );
}
break;

case ELEMENT_NODE:
if ( isTinyMCEBogusElement( node ) ) {
break;
}

if ( ! isTinyMCEBogusWrapperElement( node ) ) {
fragment.appendChild( createHTMLElementFromTinyMCEElement( node ) );
} else if ( node.hasChildNodes() ) {
fragment.appendChild( domToDOM( node.childNodes ) );
}
break;
}
}

return fragment;
}

/**
* Transforms an array of DOM Elements to the given format.
*
Expand All @@ -192,5 +238,8 @@ export function domToFormat( value, format ) {

case 'children':
return domToBlockChildren( value );

case 'dom':
return domToDOM( value );
}
}
17 changes: 15 additions & 2 deletions packages/editor/src/components/rich-text/tinymce.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { BACKSPACE, DELETE } from '@wordpress/keycodes';
* Internal dependencies
*/
import { diffAriaProps, pickAriaProps } from './aria';
import { valueToString } from './format';
import { valueToString, domToFormat } from './format';

/**
* Determines whether we need a fix to provide `input` events for contenteditable.
Expand Down Expand Up @@ -96,10 +96,13 @@ function applyInternetExplorerInputFix( editorNode ) {
}

const IS_PLACEHOLDER_VISIBLE_ATTR_NAME = 'data-is-placeholder-visible';

export default class TinyMCE extends Component {
constructor() {
super();
super( ...arguments );

this.bindEditorNode = this.bindEditorNode.bind( this );
this.resetDOMState = this.resetDOMState.bind( this );
}

componentDidMount() {
Expand Down Expand Up @@ -149,6 +152,15 @@ export default class TinyMCE extends Component {
}
}

resetDOMState() {
const cleanFragment = domToFormat( this.editorNode.childNodes, 'dom' );
while ( this.editorNode.firstChild ) {
this.editorNode.removeChild( this.editorNode.firstChild );
}

this.editorNode.appendChild( cleanFragment );
}

initialize() {
const settings = this.props.getSettings( {
theme: false,
Expand Down Expand Up @@ -215,6 +227,7 @@ export default class TinyMCE extends Component {
...ariaProps,
className: classnames( className, 'editor-rich-text__tinymce' ),
contentEditable: true,
onBlur: this.resetDOMState,
[ IS_PLACEHOLDER_VISIBLE_ATTR_NAME ]: isPlaceholderVisible,
ref: this.bindEditorNode,
style,
Expand Down
10 changes: 10 additions & 0 deletions test/e2e/specs/__snapshots__/splitting-merging.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,13 @@ exports[`splitting and merging blocks should split and merge paragraph blocks us
<p>BeforeSecond:Second</p>
<!-- /wp:paragraph -->"
`;

exports[`splitting and merging blocks should split without inline formatting 1`] = `
"<!-- wp:paragraph -->
<p><strong>Foo</strong></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Bar</p>
<!-- /wp:paragraph -->"
`;
13 changes: 13 additions & 0 deletions test/e2e/specs/splitting-merging.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,17 @@ describe( 'splitting and merging blocks', () => {

expect( await getEditedPostContent() ).toMatchSnapshot();
} );

it( 'should split without inline formatting', async () => {
// Regression test: Previously, splitting while at the extent of an
// inline boundary and continuing to type would revert caret position
// back inside the inline boundary.
await insertBlock( 'Paragraph' );
await pressWithModifier( META_KEY, 'b' );
await page.keyboard.type( 'Foo' );
await page.keyboard.press( 'Enter' );
await page.keyboard.type( 'Bar' );

expect( await getEditedPostContent() ).toMatchSnapshot();
} );
} );
9 changes: 5 additions & 4 deletions test/e2e/specs/writing-flow.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ describe( 'adding blocks', () => {
await page.keyboard.press( 'ArrowLeft' );
await page.keyboard.type( 'After' );

// Arrow right from end of first should traverse to second, *BEFORE*
// the bolded text. Another press should move within inline boundary.
await pressTimes( 'ArrowRight', 2 );
// Arrow right from end of first should traverse to second, inside the
// bolded text.
await page.keyboard.press( 'ArrowRight' );
await page.keyboard.type( 'Inside' );

// Arrow left from end of beginning of inline boundary should move to
Expand All @@ -131,7 +131,8 @@ describe( 'adding blocks', () => {
await page.keyboard.press( 'ArrowRight' );
await page.keyboard.press( 'ArrowLeft' );

// Should be after the inline boundary again.
// Should be inside the inline boundary, so navigate out.
await page.keyboard.press( 'ArrowRight' );
await page.keyboard.type( 'After' );

// Finally, ensure that ArrowRight from end of unbolded text moves to
Expand Down