diff --git a/src/element/textWysiwyg.test.tsx b/src/element/textWysiwyg.test.tsx index f234991da..7de71198b 100644 --- a/src/element/textWysiwyg.test.tsx +++ b/src/element/textWysiwyg.test.tsx @@ -3,7 +3,11 @@ import ExcalidrawApp from "../excalidraw-app"; import { GlobalTestState, render, screen } from "../tests/test-utils"; import { Keyboard, Pointer, UI } from "../tests/helpers/ui"; import { CODES, KEYS } from "../keys"; -import { fireEvent } from "../tests/test-utils"; +import { + fireEvent, + mockBoundingClientRect, + restoreOriginalGetBoundingClientRect, +} from "../tests/test-utils"; import { queryByText } from "@testing-library/react"; import { FONT_FAMILY } from "../constants"; @@ -221,11 +225,19 @@ describe("textWysiwyg", () => { describe("Test container-unbound text", () => { const { h } = window; + const dimensions = { height: 400, width: 800 }; let textarea: HTMLTextAreaElement; let textElement: ExcalidrawTextElement; + + beforeAll(() => { + mockBoundingClientRect(dimensions); + }); + beforeEach(async () => { await render(); + //@ts-ignore + h.app.refreshDeviceState(h.app.excalidrawContainerRef.current!); textElement = UI.createElement("text"); @@ -235,6 +247,10 @@ describe("textWysiwyg", () => { )!; }); + afterAll(() => { + restoreOriginalGetBoundingClientRect(); + }); + it("should add a tab at the start of the first line", () => { const event = new KeyboardEvent("keydown", { key: KEYS.TAB }); textarea.value = "Line#1\nLine#2"; @@ -433,6 +449,27 @@ describe("textWysiwyg", () => { ); expect(h.state.zoom.value).toBe(1); }); + + it("text should never go beyond max width", async () => { + UI.clickTool("text"); + mouse.clickAt(750, 300); + + textarea = document.querySelector( + ".excalidraw-textEditorContainer > textarea", + )!; + fireEvent.change(textarea, { + target: { + value: + "Excalidraw is an opensource virtual collaborative whiteboard for sketching hand-drawn like diagrams!", + }, + }); + + textarea.dispatchEvent(new Event("input")); + await new Promise((cb) => setTimeout(cb, 0)); + textarea.blur(); + expect(textarea.style.width).toBe("792px"); + expect(h.elements[0].width).toBe(1000); + }); }); describe("Test container-bound text", () => { @@ -631,11 +668,11 @@ describe("textWysiwyg", () => { ["freedraw", "line"].forEach((type: any) => { it(`shouldn't create text element when pressing 'Enter' key on ${type} `, async () => { h.elements = []; - const elemnet = UI.createElement(type, { + const element = UI.createElement(type, { width: 100, height: 50, }); - API.setSelectedElements([elemnet]); + API.setSelectedElements([element]); Keyboard.keyPress(KEYS.ENTER); expect(h.elements.length).toBe(1); }); diff --git a/src/element/textWysiwyg.tsx b/src/element/textWysiwyg.tsx index 3d43aa87a..0f7d66437 100644 --- a/src/element/textWysiwyg.tsx +++ b/src/element/textWysiwyg.tsx @@ -179,6 +179,10 @@ export const textWysiwyg = ({ let textElementHeight = Math.max(updatedTextElement.height, maxHeight); if (container && updatedTextElement.containerId) { + textElementHeight = Math.min( + getMaxContainerHeight(container), + textElementHeight, + ); if (isArrowElement(container)) { const boundTextCoords = LinearElementEditor.getBoundTextElementPosition( @@ -187,6 +191,8 @@ export const textWysiwyg = ({ ); coordX = boundTextCoords.x; coordY = boundTextCoords.y; + } else { + coordX = Math.max(coordX, getContainerCoords(container).x); } const propertiesUpdated = textPropertiesUpdated( updatedTextElement, @@ -294,8 +300,14 @@ export const textWysiwyg = ({ const lineHeight = updatedTextElement.containerId ? approxLineHeight : eMetrics.height / lines.length; + let transformWidth = updatedTextElement.width; if (!container) { maxWidth = (appState.width - 8 - viewportX) / appState.zoom.value; + textElementWidth = Math.min(textElementWidth, maxWidth); + } else if (isFirefox || isSafari) { + // As firefox, Safari needs little higher dimensions on DOM + textElementWidth += 0.5; + transformWidth += 0.5; } // Horizontal offset in case updatedTextElement has a non-WYSIWYG subtype const offWidth = container @@ -312,13 +324,6 @@ export const textWysiwyg = ({ ? offWidth / 2 : 0; const { width: w, height: h } = updatedTextElement; - - let transformWidth = updatedTextElement.width; - // As firefox, Safari needs little higher dimensions on DOM - if (isFirefox || isSafari) { - textElementWidth += 0.5; - transformWidth += 0.5; - } // Make sure text editor height doesn't go beyond viewport const editorMaxHeight = (appState.height - viewportY) / appState.zoom.value; diff --git a/src/renderer/renderElement.ts b/src/renderer/renderElement.ts index f45a15884..6dc2afc23 100644 --- a/src/renderer/renderElement.ts +++ b/src/renderer/renderElement.ts @@ -1307,7 +1307,6 @@ export const renderElementToSvg = ( ); const lines = element.text.replace(/\r\n?/g, "\n").split("\n"); const lineHeight = element.height / lines.length; - const verticalOffset = element.height; const horizontalOffset = element.textAlign === "center" ? element.width / 2 @@ -1325,13 +1324,14 @@ export const renderElementToSvg = ( const text = svgRoot.ownerDocument!.createElementNS(SVG_NS, "text"); text.textContent = lines[i]; text.setAttribute("x", `${horizontalOffset}`); - text.setAttribute("y", `${(i + 1) * lineHeight - verticalOffset}`); + text.setAttribute("y", `${i * lineHeight}`); text.setAttribute("font-family", getFontFamilyString(element)); text.setAttribute("font-size", `${element.fontSize}px`); text.setAttribute("fill", element.strokeColor); text.setAttribute("text-anchor", textAnchor); text.setAttribute("style", "white-space: pre;"); text.setAttribute("direction", direction); + text.setAttribute("dominant-baseline", "text-before-edge"); node.appendChild(text); } root.appendChild(node);