diff --git a/src/element/textWysiwyg.test.tsx b/src/element/textWysiwyg.test.tsx index 755a76aa..cae570be 100644 --- a/src/element/textWysiwyg.test.tsx +++ b/src/element/textWysiwyg.test.tsx @@ -439,7 +439,7 @@ describe("textWysiwyg", () => { it("should paste text correctly", async () => { Keyboard.keyPress(KEYS.ENTER); await new Promise((r) => setTimeout(r, 0)); - const text = "A quick brown fox jumps over the lazy dog."; + let text = "A quick brown fox jumps over the lazy dog."; //@ts-ignore textarea.onpaste({ @@ -453,6 +453,23 @@ describe("textWysiwyg", () => { await new Promise((cb) => setTimeout(cb, 0)); textarea.blur(); expect(textElement.text).toBe(text); + + Keyboard.keyPress(KEYS.ENTER); + await new Promise((r) => setTimeout(r, 0)); + text = "Hello this text should get merged with the existing one"; + //@ts-ignore + textarea.onpaste({ + preventDefault: () => {}, + //@ts-ignore + clipboardData: { + getData: () => text, + }, + }); + await new Promise((cb) => setTimeout(cb, 0)); + textarea.blur(); + expect(textElement.text).toMatchInlineSnapshot( + `"A quick brown fox jumps over the lazy dog.Hello this text should get merged with the existing one"`, + ); }); }); @@ -905,9 +922,11 @@ describe("textWysiwyg", () => { ) as HTMLTextAreaElement; await new Promise((r) => setTimeout(r, 0)); const font = "20px Cascadia, width: Segoe UI Emoji" as FontString; + let text = + "Wikipedia is hosted by the Wikimedia Foundation, a non-profit organization that also hosts a range of other projects."; - const wrappedText = textElementUtils.wrapText( - "Wikipedia is hosted by the Wikimedia Foundation, a non-profit organization that also hosts a range of other projects.", + let wrappedText = textElementUtils.wrapText( + text, font, getMaxContainerWidth(rectangle), ); @@ -926,17 +945,94 @@ describe("textWysiwyg", () => { preventDefault: () => {}, //@ts-ignore clipboardData: { - getData: () => - "Wikipedia is hosted by the Wikimedia Foundation, a non-profit organization that also hosts a range of other projects.", + getData: () => text, }, }); await new Promise((cb) => setTimeout(cb, 0)); editor.blur(); - expect(rectangle.width).toBe(110); + expect(rectangle.width).toBe(100); expect(rectangle.height).toBe(210); - const textElement = h.elements[1] as ExcalidrawTextElement; - expect(textElement.text).toBe(wrappedText); + expect((h.elements[1] as ExcalidrawTextElement).text) + .toMatchInlineSnapshot(` + "Wikipedi + a is + hosted + by the + Wikimedi + a + Foundati + on, a + non-prof + it + organiza + tion + that + also + hosts a + range of + other + projects + ." + `); + expect( + (h.elements[1] as ExcalidrawTextElement).originalText, + ).toMatchInlineSnapshot( + `"Wikipedia is hosted by the Wikimedia Foundation, a non-profit organization that also hosts a range of other projects."`, + ); + + text = "Hello this text should get merged with the existing one"; + wrappedText = textElementUtils.wrapText( + text, + font, + getMaxContainerWidth(rectangle), + ); + //@ts-ignore + editor.onpaste({ + preventDefault: () => {}, + //@ts-ignore + clipboardData: { + getData: () => text, + }, + }); + + await new Promise((cb) => setTimeout(cb, 0)); + editor.blur(); + expect((h.elements[1] as ExcalidrawTextElement).text) + .toMatchInlineSnapshot(` + "Wikipedi + a is + hosted + by the + Wikimedi + a + Foundati + on, a + non-prof + it + organiza + tion + that + also + hosts a + range of + other + projects + .Hello + this + text + should + get + merged + with the + existing + one" + `); + expect( + (h.elements[1] as ExcalidrawTextElement).originalText, + ).toMatchInlineSnapshot( + `"Wikipedia is hosted by the Wikimedia Foundation, a non-profit organization that also hosts a range of other projects.Hello this text should get merged with the existing one"`, + ); }); }); }); diff --git a/src/element/textWysiwyg.tsx b/src/element/textWysiwyg.tsx index f208c034..20bd187a 100644 --- a/src/element/textWysiwyg.tsx +++ b/src/element/textWysiwyg.tsx @@ -284,20 +284,30 @@ export const textWysiwyg = ({ return; } const data = normalizeText(clipboardData.text); + if (!data) { + return; + } + + const text = editable.value; + const start = Math.min(editable.selectionStart, editable.selectionEnd); + const end = Math.max(editable.selectionStart, editable.selectionEnd); + const newText = `${text.substring(0, start)}${data}${text.substring( + end, + )}`; + const container = getContainerElement(element); const font = getFontString({ fontSize: app.state.currentItemFontSize, fontFamily: app.state.currentItemFontFamily, }); - const wrappedText = container - ? wrapText(data, font, getMaxContainerWidth(container)) - : data; + const wrappedText = container + ? wrapText(newText, font, getMaxContainerWidth(container)) + : newText; const dimensions = measureText(wrappedText, font); editable.style.height = `${dimensions.height}px`; - - onChange(wrappedText); + onChange(newText); }; editable.oninput = () => { const updatedTextElement = Scene.getScene(element)?.getElement(