Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: iOS Autocorrect strips formatting by reporting wrong dataType #5789

Merged
merged 7 commits into from
Jan 16, 2025
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
Loading