Skip to content

Commit

Permalink
fix: iOS Autocorrect strips formatting by reporting wrong dataType (f…
Browse files Browse the repository at this point in the history
…acebook#5789)

Co-authored-by: Bob Ippolito <[email protected]>
  • Loading branch information
redstar504 and etrepum authored Jan 16, 2025
1 parent 136a565 commit 940435d
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 3 deletions.
10 changes: 7 additions & 3 deletions packages/lexical-clipboard/src/clipboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,12 @@ export function $insertDataTransferForRichText(
}

const htmlString = dataTransfer.getData('text/html');
if (htmlString) {
const plainString = dataTransfer.getData('text/plain');

// Skip HTML handling if it matches the plain text representation.
// This avoids unnecessary processing for plain text strings created by
// iOS Safari autocorrect, which incorrectly includes a `text/html` type.
if (htmlString && plainString !== htmlString) {
try {
const parser = new DOMParser();
const dom = parser.parseFromString(
Expand All @@ -165,8 +170,7 @@ export function $insertDataTransferForRichText(
// Multi-line plain text in rich text mode pasted as separate paragraphs
// instead of single paragraph with linebreaks.
// Webkit-specific: Supports read 'text/uri-list' in clipboard.
const text =
dataTransfer.getData('text/plain') || dataTransfer.getData('text/uri-list');
const text = plainString || dataTransfer.getData('text/uri-list');
if (text != null) {
if ($isRangeSelection(selection)) {
const parts = text.split(/(\r?\n|\t)/);
Expand Down
30 changes: 30 additions & 0 deletions packages/lexical/src/__tests__/unit/HTMLCopyAndPaste.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import {$insertDataTransferForRichText} from '@lexical/clipboard';
import {$patchStyleText} from '@lexical/selection';
import {
$createParagraphNode,
$getRoot,
Expand Down Expand Up @@ -110,6 +111,35 @@ describe('HTMLCopyAndPaste tests', () => {
expect(testEnv.innerHTML).toBe(testCase.expectedHTML);
});
});

test('iOS fix: Word predictions should be handled as plain text to maintain selection formatting', async () => {
const {editor} = testEnv;

const dataTransfer = new DataTransferMock();

// we simulate choosing an iOS Safari `autocorrect` or `word prediction`
// which pastes the word into the editor with both the `text/plain` and `text/html` data types
dataTransfer.setData('text/plain', 'Prediction');
dataTransfer.setData('text/html', 'Prediction');

// to compensate, the clipboard content will only be inserted as HTML if the `text/html` content differs from the `text/plain` content
await editor.update(() => {
const selection = $getSelection();
invariant(
$isRangeSelection(selection),
'isRangeSelection(selection)',
);
$patchStyleText(selection, {
'background-color': 'rgb(255,170,45)',
});
$insertDataTransferForRichText(dataTransfer, selection, editor);
});

// the editor's selection formatting is maintained because the text has been inserted as plain text
expect(testEnv.innerHTML).toBe(
'<p dir="ltr"><span style="background-color: rgb(255, 170, 45);" data-lexical-text="true">Prediction</span></p>',
);
});
},
{
namespace: 'test',
Expand Down

0 comments on commit 940435d

Please sign in to comment.