From e201e79cd086df6e58c98e1be7cfa060dac70d19 Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 8 Nov 2022 19:50:41 +0530 Subject: [PATCH] fix: compute dimensions of container correctly when text pasted on container (#5845) * fix: compute dimensions of container correctly when text pasted on container * add test * remove only --- src/element/newElement.ts | 4 +--- src/element/textElement.ts | 6 ++--- src/element/textWysiwyg.test.tsx | 41 ++++++++++++++++++++++++++++++++ src/element/textWysiwyg.tsx | 27 +++++++++++++++++++++ 4 files changed, 71 insertions(+), 7 deletions(-) diff --git a/src/element/newElement.ts b/src/element/newElement.ts index 2882bae2..5d57d6bf 100644 --- a/src/element/newElement.ts +++ b/src/element/newElement.ts @@ -169,8 +169,7 @@ const getAdjustedDimensions = ( let maxWidth = null; const container = getContainerElement(element); if (container) { - const containerDims = getContainerDims(container); - maxWidth = containerDims.width - BOUND_TEXT_PADDING * 2; + maxWidth = getMaxContainerWidth(container); } const { width: nextWidth, @@ -258,7 +257,6 @@ export const refreshTextDimensions = ( ) => { const container = getContainerElement(textElement); if (container) { - // text = wrapText(text, getFontString(textElement), container.width); text = wrapText( text, getFontString(textElement), diff --git a/src/element/textElement.ts b/src/element/textElement.ts index b1751cb6..893a172c 100644 --- a/src/element/textElement.ts +++ b/src/element/textElement.ts @@ -19,13 +19,12 @@ export const redrawTextBoundingBox = ( ) => { let maxWidth = undefined; let text = textElement.text; - if (container) { maxWidth = getMaxContainerWidth(container); text = wrapText( textElement.originalText, getFontString(textElement), - getMaxContainerWidth(container), + maxWidth, ); } const metrics = measureText( @@ -230,10 +229,9 @@ export const measureText = ( const baseline = span.offsetTop + span.offsetHeight; // Since span adds 1px extra width to the container const width = container.offsetWidth + 1; - const height = container.offsetHeight; - document.body.removeChild(container); + document.body.removeChild(container); return { width, height, baseline }; }; diff --git a/src/element/textWysiwyg.test.tsx b/src/element/textWysiwyg.test.tsx index 436b2461..659495d6 100644 --- a/src/element/textWysiwyg.test.tsx +++ b/src/element/textWysiwyg.test.tsx @@ -10,11 +10,13 @@ import { BOUND_TEXT_PADDING, FONT_FAMILY } from "../constants"; import { ExcalidrawTextElement, ExcalidrawTextElementWithContainer, + FontString, } from "./types"; import * as textElementUtils from "./textElement"; import { API } from "../tests/helpers/api"; import { mutateElement } from "./mutateElement"; import { resize } from "../tests/utils"; +import { getMaxContainerWidth } from "./newElement"; // Unmount ReactDOM from root ReactDOM.unmountComponentAtNode(document.getElementById("root")!); @@ -876,5 +878,44 @@ describe("textWysiwyg", () => { ] `); }); + + it("should compute the dimensions correctly when text pasted", async () => { + Keyboard.keyPress(KEYS.ENTER); + const editor = document.querySelector( + ".excalidraw-textEditorContainer > textarea", + ) as HTMLTextAreaElement; + await new Promise((r) => setTimeout(r, 0)); + const font = "20px Cascadia, width: Segoe UI Emoji" as FontString; + + const wrappedText = textElementUtils.wrapText( + "Wikipedia is hosted by the Wikimedia Foundation, a non-profit organization that also hosts a range of other projects.", + font, + getMaxContainerWidth(rectangle), + ); + + jest + .spyOn(textElementUtils, "measureText") + .mockImplementation((text, font, maxWidth) => { + if (text === wrappedText) { + return { width: rectangle.width, height: 200, baseline: 30 }; + } + return { width: 0, height: 0, baseline: 0 }; + }); + + //@ts-ignore + editor.onpaste({ + preventDefault: () => {}, + //@ts-ignore + clipboardData: { + getData: () => + "Wikipedia is hosted by the Wikimedia Foundation, a non-profit organization that also hosts a range of other projects.", + }, + }); + + await new Promise((cb) => setTimeout(cb, 0)); + editor.blur(); + expect(rectangle.width).toBe(110); + expect(rectangle.height).toBe(210); + }); }); }); diff --git a/src/element/textWysiwyg.tsx b/src/element/textWysiwyg.tsx index b62912b6..50387a7a 100644 --- a/src/element/textWysiwyg.tsx +++ b/src/element/textWysiwyg.tsx @@ -20,6 +20,7 @@ import { getBoundTextElementId, getContainerDims, getContainerElement, + measureText, wrapText, } from "./textElement"; import { @@ -29,6 +30,7 @@ import { import { actionZoomIn, actionZoomOut } from "../actions/actionCanvas"; import App from "../components/App"; import { getMaxContainerWidth } from "./newElement"; +import { parseClipboard } from "../clipboard"; const normalizeText = (text: string) => { return ( @@ -275,6 +277,31 @@ export const textWysiwyg = ({ updateWysiwygStyle(); if (onChange) { + editable.onpaste = async (event) => { + event.preventDefault(); + const clipboardData = await parseClipboard(event); + if (!clipboardData.text) { + return; + } + const data = normalizeText(clipboardData.text); + const container = getContainerElement(element); + + const font = getFontString({ + fontSize: app.state.currentItemFontSize, + fontFamily: app.state.currentItemFontFamily, + }); + + const wrappedText = wrapText( + data, + font, + getMaxContainerWidth(container!), + ); + const dimensions = measureText(wrappedText, font); + editable.style.height = `${dimensions.height}px`; + if (data) { + onChange(wrappedText); + } + }; editable.oninput = () => { const updatedTextElement = Scene.getScene(element)?.getElement( id,