Skip to content

Commit

Permalink
[RNMobile] Fix losing undo/redo history when using non-breaking space…
Browse files Browse the repository at this point in the history
… HTML entity (#57652)

* Stop removing non-breaking space HTML entity

The non-breaking space HTML entity is part of the RichText content, so we shouldn't remove it to preserve the original value, especially when pasting HTML. Otherwise, the removal leads to breaking the undo/redo history.

* Use `render` from test helpers in RichText unit tests

* Rename describe section in RichText unit tests

* Use `RichTextData.empty` to create empty value in RichText unit tests

* Add test case to cover non-breaking space HTML entity

* Update `describe` description in RichText unit tests

* Update snapshots of `RichText` unit tests
  • Loading branch information
fluiddot authored Jan 10, 2024
1 parent 5d063e3 commit 5dcd9d1
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,6 @@ import { getFormatColors } from './get-format-colors';
import styles from './style.scss';
import ToolbarButtonWithOptions from './toolbar-button-with-options';

const unescapeSpaces = ( text ) => {
return text.replace( / | /gi, ' ' );
};

// The flattened color palettes array is memoized to ensure that the same array instance is
// returned for the colors palettes. This value might be used as a prop, so having the same
// instance will prevent unnecessary re-renders of the RichText component.
Expand Down Expand Up @@ -318,7 +314,7 @@ export class RichText extends Component {
}

const contentWithoutRootTag = this.removeRootTagsProducedByAztec(
unescapeSpaces( event.nativeEvent.text )
event.nativeEvent.text
);
// On iOS, onChange can be triggered after selection changes, even though there are no content changes.
if ( contentWithoutRootTag === this.value.toString() ) {
Expand All @@ -333,7 +329,7 @@ export class RichText extends Component {

onTextUpdate( event ) {
const contentWithoutRootTag = this.removeRootTagsProducedByAztec(
unescapeSpaces( event.nativeEvent.text )
event.nativeEvent.text
);

this.debounceCreateUndoLevel();
Expand Down Expand Up @@ -660,7 +656,7 @@ export class RichText extends Component {

// Check and dicsard stray event, where the text and selection is equal to the ones already cached.
const contentWithoutRootTag = this.removeRootTagsProducedByAztec(
unescapeSpaces( event.nativeEvent.text )
event.nativeEvent.text
);
if (
contentWithoutRootTag === this.value.toString() &&
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<RichText/> Font Size renders component with style and font size 1`] = `
exports[`<RichText/> when applying the font size renders component with style and font size 1`] = `
"<!-- wp:paragraph {"style":{"color":{"text":"#fcb900"},"typography":{"fontSize":35.56}}} -->
<p class="has-text-color" style="color:#fcb900;font-size:35.56px">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed imperdiet ut nibh vitae ornare. Sed auctor nec augue at blandit.</p>
<!-- /wp:paragraph -->"
`;

exports[`<RichText/> Font Size should update the font size when style prop with font size property is provided 1`] = `
exports[`<RichText/> when applying the font size should update the font size when style prop with font size property is provided 1`] = `
<View
style={
[
Expand Down Expand Up @@ -42,7 +42,7 @@ exports[`<RichText/> Font Size should update the font size when style prop with
</View>
`;

exports[`<RichText/> Font Size should update the font size with decimals when style prop with font size property is provided 1`] = `
exports[`<RichText/> when applying the font size should update the font size with decimals when style prop with font size property is provided 1`] = `
<View
style={
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,20 @@ import { Dimensions } from 'react-native';
import {
fireEvent,
getEditorHtml,
render,
initializeEditor,
render,
screen,
} from 'test/helpers';

/**
* WordPress dependencies
*/
import { select } from '@wordpress/data';
import { store as richTextStore, RichTextData } from '@wordpress/rich-text';
import {
store as richTextStore,
RichTextData,
__unstableCreateElement,
} from '@wordpress/rich-text';
import { coreBlocks } from '@wordpress/block-library';
import {
getBlockTypes,
Expand Down Expand Up @@ -83,11 +88,11 @@ describe( '<RichText/>', () => {
} );
} );

describe( 'when changes arrive from Aztec', () => {
describe( 'when the value changes', () => {
it( 'should avoid updating attributes when values are equal', async () => {
const handleChange = jest.fn();
const defaultEmptyValue = new RichTextData();
const screen = render(
const defaultEmptyValue = RichTextData.empty();
render(
<RichText
onChange={ handleChange }
value={ defaultEmptyValue }
Expand All @@ -101,9 +106,46 @@ describe( '<RichText/>', () => {

expect( handleChange ).not.toHaveBeenCalled();
} );

it( 'should preserve non-breaking space HTML entity', () => {
const onChange = jest.fn();
const onSelectionChange = jest.fn();
// The initial value is created using an HTML element to preserve
// the HTML entity.
const initialValue = RichTextData.fromHTMLElement(
__unstableCreateElement( document, '&nbsp;' )
);
render(
<RichText
onChange={ onChange }
onSelectionChange={ onSelectionChange }
value={ initialValue }
__unstableIsSelected
/>
);

// Trigger selection event with same text value as initial.
fireEvent(
screen.getByLabelText( /Text input/ ),
'onSelectionChange',
0,
0,
initialValue.toString(),
{
nativeEvent: {
eventCount: 0,
target: undefined,
text: initialValue.toString(),
},
}
);

expect( onChange ).not.toHaveBeenCalled();
expect( onSelectionChange ).toHaveBeenCalled();
} );
} );

describe( 'Font Size', () => {
describe( 'when applying the font size', () => {
it( 'should display rich text at the DEFAULT font size.', () => {
// Arrange.
const expectedFontSize = 16;
Expand Down Expand Up @@ -259,7 +301,7 @@ describe( '<RichText/>', () => {
const fontSize = '10';
const style = { fontSize: '12' };
// Act.
const screen = render( <RichText fontSize={ fontSize } /> );
render( <RichText fontSize={ fontSize } /> );
screen.update( <RichText fontSize={ fontSize } style={ style } /> );
// Assert.
expect( screen.toJSON() ).toMatchSnapshot();
Expand All @@ -281,7 +323,7 @@ describe( '<RichText/>', () => {
const fontSize = '10';
const style = { fontSize: '12.56px' };
// Act.
const screen = render( <RichText fontSize={ fontSize } /> );
render( <RichText fontSize={ fontSize } /> );
screen.update( <RichText fontSize={ fontSize } style={ style } /> );
// Assert.
expect( screen.toJSON() ).toMatchSnapshot();
Expand Down

0 comments on commit 5dcd9d1

Please sign in to comment.