Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,5 @@ figcaption.block-editor-rich-text__editable [data-rich-text-placeholder]::before
// A background, not a pseudo-element: generated content adds a caret stop in
// Firefox.
.is-selected .non-breaking-space {
background: radial-gradient(circle, $alert-red 2px, transparent 2.5px) center / 100% 100% no-repeat;
background: radial-gradient(circle, var(--wp-admin-theme-color) 2px, transparent 2.5px) center / 100% 100% no-repeat;
}
1 change: 1 addition & 0 deletions packages/format-library/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Bug Fixes

- Non-breaking space: Remove a non-breaking space instead of replacing it when HTML would collapse the space ([#85873](https://github.com/WordPress/gutenberg/pull/85873)).
- Math: Let keyboard users move focus out of the LaTeX field when the formula has an error, instead of sending focus back to the field ([#86027](https://github.com/WordPress/gutenberg/pull/86027)).
- Non-breaking space: Label the replace button "Replace non-breaking space" when it replaces only one ([#85777](https://github.com/WordPress/gutenberg/pull/85777)).

Expand Down
22 changes: 19 additions & 3 deletions packages/format-library/src/non-breaking-space/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { __ } from '@wordpress/i18n';
import { applyFormat, insert, useAnchor } from '@wordpress/rich-text';
import { applyFormat, insert, remove, useAnchor } from '@wordpress/rich-text';
import type { RichTextValue } from '@wordpress/rich-text';
import {
RichTextShortcut,
Expand Down Expand Up @@ -71,13 +71,29 @@ export const nonBreakingSpace = {

const [ from, to ] = range;
const { text } = value;
onChange( {
let newValue: RichTextValue = {
...value,
text:
text.slice( 0, from ) +
text.slice( from, to ).replaceAll( NBSP, ' ' ) +
text.slice( to ),
} );
};

// HTML collapses a space next to another space or at the start or
// end of a line, so remove the non-breaking space there instead.
for ( let index = to - 1; index >= from; index-- ) {
const isCollapsed = [
newValue.text[ index - 1 ],
newValue.text[ index + 1 ],
].some( ( character ) =>
[ undefined, ' ', '\n' ].includes( character )
);
if ( text[ index ] === NBSP && isCollapsed ) {
newValue = remove( newValue, index, index + 1 );
}
}

onChange( newValue );
onFocus();
}

Expand Down
63 changes: 63 additions & 0 deletions test/e2e/specs/editor/various/rich-text.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1091,12 +1091,75 @@
exact: true,
} )
.click();
await expect.poll( editor.getBlocks ).toMatchObject( [
{
name: 'core/paragraph',
attributes: { content: 'one two' },
},
] );
} );

test( 'should remove non-breaking spaces that would collapse when replaced', async ( {

Check notice on line 1102 in test/e2e/specs/editor/various/rich-text.spec.js

View workflow job for this annotation

GitHub Actions / Run new e2e tests on trunk

This test fails on trunk, a good sign that it catches the bug.
page,
editor,
pageUtils,
} ) => {
const toolbar = page.getByRole( 'toolbar', { name: 'Block tools' } );

await editor.insertBlock( {
name: 'core/paragraph',
attributes: { content: 'one \u00a0 two' },
} );
await editor.canvas
.getByRole( 'document', { name: 'Block: Paragraph' } )
.click();
await pageUtils.pressKeys( 'End' );
// After the non-breaking space.
await pageUtils.pressKeys( 'ArrowLeft', { times: 4 } );
await editor.showBlockToolbar();
await toolbar
.getByRole( 'button', {
name: 'Replace non-breaking space',
exact: true,
} )
.click();
await expect.poll( editor.getBlocks ).toMatchObject( [
{
name: 'core/paragraph',
attributes: { content: 'one two' },
},
] );

await page.keyboard.type( '!' );
await expect.poll( editor.getBlocks ).toMatchObject( [
{
name: 'core/paragraph',
attributes: { content: 'one ! two' },
},
] );

await editor.insertBlock( {
name: 'core/paragraph',
attributes: { content: '\u00a0one\u00a0\u00a0two\u00a0' },
} );
await editor.canvas
.getByRole( 'document', { name: 'Block: Paragraph' } )
.last()
.click();
await pageUtils.pressKeys( 'primary+a' );
await editor.showBlockToolbar();
await toolbar
.getByRole( 'button', {
name: 'Replace non-breaking spaces',
exact: true,
} )
.click();
await expect
.poll( editor.getBlocks )
.toMatchObject( [
{ attributes: { content: 'one ! two' } },
{ attributes: { content: 'one two' } },
] );
} );

test( 'should delete all content of a block', async ( {
Expand Down
Loading