Skip to content

Commit

Permalink
RichText: bypass paste filters for internal paste (#27967)
Browse files Browse the repository at this point in the history
* RichText: bypass interal paste

* Fix e2e tests

* Add e2e test

* Fix e2e tests
  • Loading branch information
ellatrix authored Feb 25, 2021
1 parent 2514a14 commit 06f741e
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 17 deletions.
55 changes: 41 additions & 14 deletions packages/block-editor/src/components/rich-text/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@ import { store as blockEditorStore } from '../../store';
const wrapperClasses = 'block-editor-rich-text';
const classes = 'block-editor-rich-text__editable';

function addActiveFormats( value, activeFormats ) {
if ( activeFormats.length ) {
let index = value.formats.length;

while ( index-- ) {
value.formats[ index ] = [
...activeFormats,
...( value.formats[ index ] || [] ),
];
}
}
}

/**
* Get the multiline tag based on the multiline prop.
*
Expand Down Expand Up @@ -412,7 +425,31 @@ function RichTextWrapper(
);

const onPaste = useCallback(
( { value, onChange, html, plainText, files, activeFormats } ) => {
( {
value,
onChange,
html,
plainText,
isInternal,
files,
activeFormats,
} ) => {
// If the data comes from a rich text instance, we can directly use it
// without filtering the data. The filters are only meant for externally
// pasted content and remove inline styles.
if ( isInternal ) {
const pastedValue = create( {
html,
multilineTag,
multilineWrapperTags:
multilineTag === 'li' ? [ 'ul', 'ol' ] : undefined,
preserveWhiteSpace,
} );
addActiveFormats( pastedValue, activeFormats );
onChange( insert( value, pastedValue ) );
return;
}

if ( pastePlainText ) {
onChange( insert( value, create( { text: plainText } ) ) );
return;
Expand Down Expand Up @@ -474,21 +511,11 @@ function RichTextWrapper(
if ( typeof content === 'string' ) {
let valueToInsert = create( { html: content } );

// If there are active formats, merge them with the pasted formats.
if ( activeFormats.length ) {
let index = valueToInsert.formats.length;

while ( index-- ) {
valueToInsert.formats[ index ] = [
...activeFormats,
...( valueToInsert.formats[ index ] || [] ),
];
}
}
addActiveFormats( valueToInsert, activeFormats );

// If the content should be multiline, we should process text
// separated by a line break as separate lines.
if ( multiline ) {
if ( multilineTag ) {
valueToInsert = replace(
valueToInsert,
/\n+/g,
Expand All @@ -511,7 +538,7 @@ function RichTextWrapper(
onSplit,
splitValue,
__unstableEmbedURLOnPaste,
multiline,
multilineTag,
preserveWhiteSpace,
pastePlainText,
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,22 @@ exports[`RichText should only mutate text data on input 1`] = `
<!-- /wp:paragraph -->"
`;

exports[`RichText should preserve internal formatting 1`] = `
"<!-- wp:paragraph -->
<p><span class=\\"has-inline-color has-cyan-bluish-gray-color\\">1</span></p>
<!-- /wp:paragraph -->"
`;
exports[`RichText should preserve internal formatting 2`] = `
"<!-- wp:paragraph -->
<p><span class=\\"has-inline-color has-cyan-bluish-gray-color\\">1</span></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><span class=\\"has-inline-color has-cyan-bluish-gray-color\\">1</span></p>
<!-- /wp:paragraph -->"
`;
exports[`RichText should return focus when pressing formatting button 1`] = `
"<!-- wp:paragraph -->
<p>Some <strong>bold</strong>.</p>
Expand Down
41 changes: 41 additions & 0 deletions packages/e2e-tests/specs/editor/various/rich-text.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
clickBlockAppender,
pressKeyWithModifier,
showBlockToolbar,
clickBlockToolbarButton,
} from '@wordpress/e2e-test-utils';

describe( 'RichText', () => {
Expand Down Expand Up @@ -374,4 +375,44 @@ describe( 'RichText', () => {

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

it( 'should preserve internal formatting', async () => {
await clickBlockAppender();

// Add text and select to color.
await page.keyboard.type( '1' );
await pressKeyWithModifier( 'primary', 'a' );
await clickBlockToolbarButton( 'More' );

const button = await page.waitForXPath(
`//button[contains(text(), 'Text color')]`
);
// Clicks may fail if the button is out of view. Assure it is before click.
await button.evaluate( ( element ) => element.scrollIntoView() );
await button.click();

// Select color other than black.
await page.keyboard.press( 'Tab' );
await page.keyboard.press( 'Enter' );

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

// Navigate to the block.
await page.keyboard.press( 'Tab' );
await pressKeyWithModifier( 'primary', 'a' );

// Copy the colored text.
await pressKeyWithModifier( 'primary', 'c' );

// Collapsed the selection to the end.
await page.keyboard.press( 'ArrowRight' );

// Create a new paragraph.
await page.keyboard.press( 'Enter' );

// Paste the colored text.
await pressKeyWithModifier( 'primary', 'v' );

expect( await getEditedPostContent() ).toMatchSnapshot();
} );
} );
23 changes: 23 additions & 0 deletions packages/rich-text/src/component/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ import { useFormatTypes } from './use-format-types';
import { useBoundaryStyle } from './use-boundary-style';
import { useInlineWarning } from './use-inline-warning';
import { insert } from '../insert';
import { slice } from '../slice';
import { getTextContent } from '../get-text-content';

/** @typedef {import('@wordpress/element').WPSyntheticEvent} WPSyntheticEvent */

Expand Down Expand Up @@ -311,6 +313,24 @@ function RichText(
} );
}

function handleCopy( event ) {
if ( isCollapsed( record.current ) ) {
return;
}

const selectedRecord = slice( record.current );
const plainText = getTextContent( selectedRecord );
const html = toHTMLString( {
value: selectedRecord,
multilineTag,
preserveWhiteSpace,
} );
event.clipboardData.setData( 'text/plain', plainText );
event.clipboardData.setData( 'text/html', html );
event.clipboardData.setData( 'rich-text', 'true' );
event.preventDefault();
}

/**
* Handles a paste event.
*
Expand Down Expand Up @@ -379,12 +399,14 @@ function RichText(

if ( onPaste ) {
const files = getFilesFromDataTransfer( clipboardData );
const isInternal = clipboardData.getData( 'rich-text' ) === 'true';

onPaste( {
value: removeEditorOnlyFormats( record.current ),
onChange: handleChange,
html,
plainText,
isInternal,
files: [ ...files ],
activeFormats,
} );
Expand Down Expand Up @@ -1078,6 +1100,7 @@ function RichText(
ref: useMergeRefs( [ forwardedRef, ref ] ),
style: defaultStyle,
className: 'rich-text',
onCopy: handleCopy,
onPaste: handlePaste,
onInput: handleInput,
onCompositionStart: handleCompositionStart,
Expand Down
12 changes: 11 additions & 1 deletion packages/rich-text/src/get-text-content.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/**
* Internal dependencies
*/
import {
OBJECT_REPLACEMENT_CHARACTER,
LINE_SEPARATOR,
} from './special-characters';

/** @typedef {import('./create').RichTextValue} RichTextValue */

/**
Expand All @@ -9,5 +17,7 @@
* @return {string} The text content.
*/
export function getTextContent( { text } ) {
return text;
return text
.replace( new RegExp( OBJECT_REPLACEMENT_CHARACTER, 'g' ), '' )
.replace( new RegExp( LINE_SEPARATOR, 'g' ), '\n' );
}
3 changes: 1 addition & 2 deletions packages/rich-text/src/insert-line-separator.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
* Internal dependencies
*/

import { getTextContent } from './get-text-content';
import { insert } from './insert';
import { LINE_SEPARATOR } from './special-characters';

Expand All @@ -24,7 +23,7 @@ export function insertLineSeparator(
startIndex = value.start,
endIndex = value.end
) {
const beforeText = getTextContent( value ).slice( 0, startIndex );
const beforeText = value.text.slice( 0, startIndex );
const previousLineSeparatorIndex = beforeText.lastIndexOf( LINE_SEPARATOR );
const previousLineSeparatorFormats =
value.replacements[ previousLineSeparatorIndex ];
Expand Down

0 comments on commit 06f741e

Please sign in to comment.