test: add more resizing tests (#7028)
Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
parent
b86184a849
commit
89a3bbddb7
@ -17,7 +17,6 @@ import {
|
|||||||
} from "./types";
|
} from "./types";
|
||||||
import { API } from "../tests/helpers/api";
|
import { API } from "../tests/helpers/api";
|
||||||
import { mutateElement } from "./mutateElement";
|
import { mutateElement } from "./mutateElement";
|
||||||
import { resize } from "../tests/utils";
|
|
||||||
import { getOriginalContainerHeightFromCache } from "./textWysiwyg";
|
import { getOriginalContainerHeightFromCache } from "./textWysiwyg";
|
||||||
|
|
||||||
// Unmount ReactDOM from root
|
// Unmount ReactDOM from root
|
||||||
@ -953,7 +952,7 @@ describe("textWysiwyg", () => {
|
|||||||
editor.blur();
|
editor.blur();
|
||||||
|
|
||||||
// should center align horizontally and vertically by default
|
// should center align horizontally and vertically by default
|
||||||
resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 100]);
|
UI.resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 100]);
|
||||||
expect([h.elements[1].x, h.elements[1].y]).toMatchInlineSnapshot(`
|
expect([h.elements[1].x, h.elements[1].y]).toMatchInlineSnapshot(`
|
||||||
[
|
[
|
||||||
85,
|
85,
|
||||||
@ -977,7 +976,7 @@ describe("textWysiwyg", () => {
|
|||||||
editor.blur();
|
editor.blur();
|
||||||
|
|
||||||
// should left align horizontally and bottom vertically after resize
|
// should left align horizontally and bottom vertically after resize
|
||||||
resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 100]);
|
UI.resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 100]);
|
||||||
expect([h.elements[1].x, h.elements[1].y]).toMatchInlineSnapshot(`
|
expect([h.elements[1].x, h.elements[1].y]).toMatchInlineSnapshot(`
|
||||||
[
|
[
|
||||||
15,
|
15,
|
||||||
@ -999,7 +998,7 @@ describe("textWysiwyg", () => {
|
|||||||
editor.blur();
|
editor.blur();
|
||||||
|
|
||||||
// should right align horizontally and top vertically after resize
|
// should right align horizontally and top vertically after resize
|
||||||
resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 100]);
|
UI.resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 100]);
|
||||||
expect([h.elements[1].x, h.elements[1].y]).toMatchInlineSnapshot(`
|
expect([h.elements[1].x, h.elements[1].y]).toMatchInlineSnapshot(`
|
||||||
[
|
[
|
||||||
374.99999999999994,
|
374.99999999999994,
|
||||||
@ -1049,7 +1048,7 @@ describe("textWysiwyg", () => {
|
|||||||
expect(rectangle.height).toBe(75);
|
expect(rectangle.height).toBe(75);
|
||||||
expect(textElement.fontSize).toBe(20);
|
expect(textElement.fontSize).toBe(20);
|
||||||
|
|
||||||
resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 50], {
|
UI.resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 50], {
|
||||||
shift: true,
|
shift: true,
|
||||||
});
|
});
|
||||||
expect(rectangle.width).toBe(200);
|
expect(rectangle.width).toBe(200);
|
||||||
@ -1189,7 +1188,7 @@ describe("textWysiwyg", () => {
|
|||||||
updateTextEditor(editor, "Hello");
|
updateTextEditor(editor, "Hello");
|
||||||
editor.blur();
|
editor.blur();
|
||||||
|
|
||||||
resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 100]);
|
UI.resize(rectangle, "ne", [rectangle.x + 100, rectangle.y - 100]);
|
||||||
expect(rectangle.height).toBeCloseTo(155, 8);
|
expect(rectangle.height).toBeCloseTo(155, 8);
|
||||||
expect(getOriginalContainerHeightFromCache(rectangle.id)).toBe(null);
|
expect(getOriginalContainerHeightFromCache(rectangle.id)).toBe(null);
|
||||||
|
|
||||||
@ -1513,28 +1512,16 @@ describe("textWysiwyg", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should bump the version of labelled arrow when label updated", async () => {
|
it("should bump the version of a labeled arrow when the label is updated", async () => {
|
||||||
await render(<Excalidraw handleKeyboardGlobally={true} />);
|
await render(<Excalidraw handleKeyboardGlobally={true} />);
|
||||||
const arrow = UI.createElement("arrow", {
|
const arrow = UI.createElement("arrow", {
|
||||||
width: 300,
|
width: 300,
|
||||||
height: 0,
|
height: 0,
|
||||||
});
|
});
|
||||||
|
await UI.editText(arrow, "Hello");
|
||||||
mouse.select(arrow);
|
|
||||||
Keyboard.keyPress(KEYS.ENTER);
|
|
||||||
let editor = getTextEditor();
|
|
||||||
await new Promise((r) => setTimeout(r, 0));
|
|
||||||
updateTextEditor(editor, "Hello");
|
|
||||||
editor.blur();
|
|
||||||
|
|
||||||
const { version } = arrow;
|
const { version } = arrow;
|
||||||
|
|
||||||
mouse.select(arrow);
|
await UI.editText(arrow, "Hello\nworld!");
|
||||||
Keyboard.keyPress(KEYS.ENTER);
|
|
||||||
editor = getTextEditor();
|
|
||||||
await new Promise((r) => setTimeout(r, 0));
|
|
||||||
updateTextEditor(editor, "Hello\nworld!");
|
|
||||||
editor.blur();
|
|
||||||
|
|
||||||
expect(arrow.version).toEqual(version + 1);
|
expect(arrow.version).toEqual(version + 1);
|
||||||
});
|
});
|
||||||
|
@ -1,13 +1,37 @@
|
|||||||
import {
|
import type { Point } from "../../types";
|
||||||
|
import type {
|
||||||
ExcalidrawElement,
|
ExcalidrawElement,
|
||||||
ExcalidrawLinearElement,
|
ExcalidrawLinearElement,
|
||||||
ExcalidrawTextElement,
|
ExcalidrawTextElement,
|
||||||
|
ExcalidrawArrowElement,
|
||||||
|
ExcalidrawRectangleElement,
|
||||||
|
ExcalidrawEllipseElement,
|
||||||
|
ExcalidrawDiamondElement,
|
||||||
|
ExcalidrawTextContainer,
|
||||||
|
ExcalidrawTextElementWithContainer,
|
||||||
} from "../../element/types";
|
} from "../../element/types";
|
||||||
|
import {
|
||||||
|
getTransformHandles,
|
||||||
|
getTransformHandlesFromCoords,
|
||||||
|
OMIT_SIDES_FOR_FRAME,
|
||||||
|
OMIT_SIDES_FOR_MULTIPLE_ELEMENTS,
|
||||||
|
TransformHandleType,
|
||||||
|
type TransformHandle,
|
||||||
|
type TransformHandleDirection,
|
||||||
|
} from "../../element/transformHandles";
|
||||||
import { KEYS } from "../../keys";
|
import { KEYS } from "../../keys";
|
||||||
import { ToolName } from "../queries/toolQueries";
|
import { type ToolName } from "../queries/toolQueries";
|
||||||
import { fireEvent, GlobalTestState } from "../test-utils";
|
import { fireEvent, GlobalTestState, screen } from "../test-utils";
|
||||||
import { mutateElement } from "../../element/mutateElement";
|
import { mutateElement } from "../../element/mutateElement";
|
||||||
import { API } from "./api";
|
import { API } from "./api";
|
||||||
|
import {
|
||||||
|
isFrameElement,
|
||||||
|
isLinearElement,
|
||||||
|
isFreeDrawElement,
|
||||||
|
isTextElement,
|
||||||
|
} from "../../element/typeChecks";
|
||||||
|
import { getCommonBounds, getElementPointsCoords } from "../../element/bounds";
|
||||||
|
import { rotatePoint } from "../../math";
|
||||||
|
|
||||||
const { h } = window;
|
const { h } = window;
|
||||||
|
|
||||||
@ -86,6 +110,29 @@ export class Keyboard {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getElementPointForSelection = (element: ExcalidrawElement): Point => {
|
||||||
|
const { x, y, width, height, angle } = element;
|
||||||
|
const target: Point = [
|
||||||
|
x +
|
||||||
|
(isLinearElement(element) || isFreeDrawElement(element) ? 0 : width / 2),
|
||||||
|
y,
|
||||||
|
];
|
||||||
|
let center: Point;
|
||||||
|
|
||||||
|
if (isLinearElement(element)) {
|
||||||
|
const bounds = getElementPointsCoords(element, element.points);
|
||||||
|
center = [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2];
|
||||||
|
} else {
|
||||||
|
center = [x + width / 2, y + height / 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTextElement(element)) {
|
||||||
|
return center;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rotatePoint(target, center, angle);
|
||||||
|
};
|
||||||
|
|
||||||
export class Pointer {
|
export class Pointer {
|
||||||
public clientX = 0;
|
public clientX = 0;
|
||||||
public clientY = 0;
|
public clientY = 0;
|
||||||
@ -199,31 +246,120 @@ export class Pointer {
|
|||||||
elements: ExcalidrawElement | ExcalidrawElement[],
|
elements: ExcalidrawElement | ExcalidrawElement[],
|
||||||
) {
|
) {
|
||||||
API.clearSelection();
|
API.clearSelection();
|
||||||
|
|
||||||
Keyboard.withModifierKeys({ shift: true }, () => {
|
Keyboard.withModifierKeys({ shift: true }, () => {
|
||||||
elements = Array.isArray(elements) ? elements : [elements];
|
elements = Array.isArray(elements) ? elements : [elements];
|
||||||
elements.forEach((element) => {
|
elements.forEach((element) => {
|
||||||
this.reset();
|
this.reset();
|
||||||
this.click(element.x, element.y);
|
this.click(...getElementPointForSelection(element));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.reset();
|
this.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
clickOn(element: ExcalidrawElement) {
|
clickOn(element: ExcalidrawElement) {
|
||||||
this.reset();
|
this.reset();
|
||||||
this.click(element.x, element.y);
|
this.click(...getElementPointForSelection(element));
|
||||||
this.reset();
|
this.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
doubleClickOn(element: ExcalidrawElement) {
|
doubleClickOn(element: ExcalidrawElement) {
|
||||||
this.reset();
|
this.reset();
|
||||||
this.doubleClick(element.x, element.y);
|
this.doubleClick(...getElementPointForSelection(element));
|
||||||
this.reset();
|
this.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mouse = new Pointer("mouse");
|
const mouse = new Pointer("mouse");
|
||||||
|
|
||||||
|
const transform = (
|
||||||
|
element: ExcalidrawElement | ExcalidrawElement[],
|
||||||
|
handle: TransformHandleType,
|
||||||
|
mouseMove: [deltaX: number, deltaY: number],
|
||||||
|
keyboardModifiers: KeyboardModifiers = {},
|
||||||
|
) => {
|
||||||
|
const elements = Array.isArray(element) ? element : [element];
|
||||||
|
mouse.select(elements);
|
||||||
|
let handleCoords: TransformHandle | undefined;
|
||||||
|
|
||||||
|
if (elements.length === 1) {
|
||||||
|
handleCoords = getTransformHandles(elements[0], h.state.zoom, "mouse")[
|
||||||
|
handle
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
const [x1, y1, x2, y2] = getCommonBounds(elements);
|
||||||
|
const isFrameSelected = elements.some(isFrameElement);
|
||||||
|
const transformHandles = getTransformHandlesFromCoords(
|
||||||
|
[x1, y1, x2, y2, (x1 + x2) / 2, (y1 + y2) / 2],
|
||||||
|
0,
|
||||||
|
h.state.zoom,
|
||||||
|
"mouse",
|
||||||
|
isFrameSelected ? OMIT_SIDES_FOR_FRAME : OMIT_SIDES_FOR_MULTIPLE_ELEMENTS,
|
||||||
|
);
|
||||||
|
handleCoords = transformHandles[handle];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!handleCoords) {
|
||||||
|
throw new Error(`There is no "${handle}" handle for this selection`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const clientX = handleCoords[0] + handleCoords[2] / 2;
|
||||||
|
const clientY = handleCoords[1] + handleCoords[3] / 2;
|
||||||
|
|
||||||
|
Keyboard.withModifierKeys(keyboardModifiers, () => {
|
||||||
|
mouse.reset();
|
||||||
|
mouse.down(clientX, clientY);
|
||||||
|
mouse.move(mouseMove[0], mouseMove[1]);
|
||||||
|
mouse.up();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const proxy = <T extends ExcalidrawElement>(
|
||||||
|
element: T,
|
||||||
|
): typeof element & {
|
||||||
|
/** Returns the actual, current element from the elements array, instead of
|
||||||
|
the proxy */
|
||||||
|
get(): typeof element;
|
||||||
|
} => {
|
||||||
|
return new Proxy(
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
get(target, prop) {
|
||||||
|
const currentElement = h.elements.find(
|
||||||
|
({ id }) => id === element.id,
|
||||||
|
) as any;
|
||||||
|
if (prop === "get") {
|
||||||
|
if (currentElement.hasOwnProperty("get")) {
|
||||||
|
throw new Error(
|
||||||
|
"trying to get `get` test property, but ExcalidrawElement seems to define its own",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return () => currentElement;
|
||||||
|
}
|
||||||
|
return currentElement[prop];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
) as any;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Tools that can be used to draw shapes */
|
||||||
|
type DrawingToolName = Exclude<ToolName, "lock" | "selection" | "eraser">;
|
||||||
|
|
||||||
|
type Element<T extends DrawingToolName> = T extends "line" | "freedraw"
|
||||||
|
? ExcalidrawLinearElement
|
||||||
|
: T extends "arrow"
|
||||||
|
? ExcalidrawArrowElement
|
||||||
|
: T extends "text"
|
||||||
|
? ExcalidrawTextElement
|
||||||
|
: T extends "rectangle"
|
||||||
|
? ExcalidrawRectangleElement
|
||||||
|
: T extends "ellipse"
|
||||||
|
? ExcalidrawEllipseElement
|
||||||
|
: T extends "diamond"
|
||||||
|
? ExcalidrawDiamondElement
|
||||||
|
: ExcalidrawElement;
|
||||||
|
|
||||||
export class UI {
|
export class UI {
|
||||||
static clickTool = (toolName: ToolName) => {
|
static clickTool = (toolName: ToolName) => {
|
||||||
fireEvent.click(GlobalTestState.renderResult.getByToolName(toolName));
|
fireEvent.click(GlobalTestState.renderResult.getByToolName(toolName));
|
||||||
@ -246,6 +382,10 @@ export class UI {
|
|||||||
fireEvent.click(element);
|
fireEvent.click(element);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static clickByTitle = (title: string) => {
|
||||||
|
fireEvent.click(screen.getByTitle(title));
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an Excalidraw element, and returns a proxy that wraps it so that
|
* Creates an Excalidraw element, and returns a proxy that wraps it so that
|
||||||
* accessing props will return the latest ones from the object existing in
|
* accessing props will return the latest ones from the object existing in
|
||||||
@ -255,16 +395,17 @@ export class UI {
|
|||||||
* If you need to get the actual element, not the proxy, call `get()` method
|
* If you need to get the actual element, not the proxy, call `get()` method
|
||||||
* on the proxy object.
|
* on the proxy object.
|
||||||
*/
|
*/
|
||||||
static createElement<T extends ToolName>(
|
static createElement<T extends DrawingToolName>(
|
||||||
type: T,
|
type: T,
|
||||||
{
|
{
|
||||||
position = 0,
|
position = 0,
|
||||||
x = position,
|
x = position,
|
||||||
y = position,
|
y = position,
|
||||||
size = 10,
|
size = 10,
|
||||||
width = size,
|
width: initialWidth = size,
|
||||||
height = width,
|
height: initialHeight = initialWidth,
|
||||||
angle = 0,
|
angle = 0,
|
||||||
|
points: initialPoints,
|
||||||
}: {
|
}: {
|
||||||
position?: number;
|
position?: number;
|
||||||
x?: number;
|
x?: number;
|
||||||
@ -273,25 +414,46 @@ export class UI {
|
|||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
angle?: number;
|
angle?: number;
|
||||||
|
points?: T extends "line" | "arrow" | "freedraw" ? Point[] : never;
|
||||||
} = {},
|
} = {},
|
||||||
): (T extends "arrow" | "line" | "freedraw"
|
): Element<T> & {
|
||||||
? ExcalidrawLinearElement
|
|
||||||
: T extends "text"
|
|
||||||
? ExcalidrawTextElement
|
|
||||||
: ExcalidrawElement) & {
|
|
||||||
/** Returns the actual, current element from the elements array, instead
|
/** Returns the actual, current element from the elements array, instead
|
||||||
of the proxy */
|
of the proxy */
|
||||||
get(): T extends "arrow" | "line" | "freedraw"
|
get(): Element<T>;
|
||||||
? ExcalidrawLinearElement
|
|
||||||
: T extends "text"
|
|
||||||
? ExcalidrawTextElement
|
|
||||||
: ExcalidrawElement;
|
|
||||||
} {
|
} {
|
||||||
|
const width = initialWidth ?? initialHeight ?? size;
|
||||||
|
const height = initialHeight ?? size;
|
||||||
|
const points: Point[] = initialPoints ?? [
|
||||||
|
[0, 0],
|
||||||
|
[width, height],
|
||||||
|
];
|
||||||
|
|
||||||
UI.clickTool(type);
|
UI.clickTool(type);
|
||||||
mouse.reset();
|
|
||||||
mouse.down(x, y);
|
if (type === "text") {
|
||||||
mouse.reset();
|
mouse.reset();
|
||||||
mouse.up(x + (width ?? height ?? size), y + (height ?? size));
|
mouse.click(x, y);
|
||||||
|
} else if ((type === "line" || type === "arrow") && points.length > 2) {
|
||||||
|
points.forEach((point) => {
|
||||||
|
mouse.reset();
|
||||||
|
mouse.click(x + point[0], y + point[1]);
|
||||||
|
});
|
||||||
|
Keyboard.keyPress(KEYS.ESCAPE);
|
||||||
|
} else if (type === "freedraw" && points.length > 2) {
|
||||||
|
const firstPoint = points[0];
|
||||||
|
mouse.reset();
|
||||||
|
mouse.down(x + firstPoint[0], y + firstPoint[1]);
|
||||||
|
points
|
||||||
|
.slice(1)
|
||||||
|
.forEach((point) => mouse.moveTo(x + point[0], y + point[1]));
|
||||||
|
mouse.upAt();
|
||||||
|
Keyboard.keyPress(KEYS.ESCAPE);
|
||||||
|
} else {
|
||||||
|
mouse.reset();
|
||||||
|
mouse.down(x, y);
|
||||||
|
mouse.reset();
|
||||||
|
mouse.up(x + width, y + height);
|
||||||
|
}
|
||||||
|
|
||||||
const origElement = h.elements[h.elements.length - 1] as any;
|
const origElement = h.elements[h.elements.length - 1] as any;
|
||||||
|
|
||||||
@ -299,25 +461,58 @@ export class UI {
|
|||||||
mutateElement(origElement, { angle });
|
mutateElement(origElement, { angle });
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Proxy(
|
return proxy(origElement);
|
||||||
{},
|
}
|
||||||
{
|
|
||||||
get(target, prop) {
|
static async editText<
|
||||||
const currentElement = h.elements.find(
|
T extends ExcalidrawTextElement | ExcalidrawTextContainer,
|
||||||
(element) => element.id === origElement.id,
|
>(element: T, text: string) {
|
||||||
) as any;
|
const openedEditor = document.querySelector<HTMLTextAreaElement>(
|
||||||
if (prop === "get") {
|
".excalidraw-textEditorContainer > textarea",
|
||||||
if (currentElement.hasOwnProperty("get")) {
|
);
|
||||||
throw new Error(
|
|
||||||
"trying to get `get` test property, but ExcalidrawElement seems to define its own",
|
if (!openedEditor) {
|
||||||
);
|
mouse.select(element);
|
||||||
}
|
Keyboard.keyPress(KEYS.ENTER);
|
||||||
return () => currentElement;
|
}
|
||||||
}
|
|
||||||
return currentElement[prop];
|
const editor =
|
||||||
},
|
openedEditor ??
|
||||||
},
|
document.querySelector<HTMLTextAreaElement>(
|
||||||
) as any;
|
".excalidraw-textEditorContainer > textarea",
|
||||||
|
);
|
||||||
|
if (!editor) {
|
||||||
|
throw new Error("Can't find wysiwyg text editor in the dom");
|
||||||
|
}
|
||||||
|
|
||||||
|
fireEvent.input(editor, { target: { value: text } });
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
|
editor.blur();
|
||||||
|
|
||||||
|
return isTextElement(element)
|
||||||
|
? element
|
||||||
|
: proxy(
|
||||||
|
h.elements[
|
||||||
|
h.elements.length - 1
|
||||||
|
] as ExcalidrawTextElementWithContainer,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static resize(
|
||||||
|
element: ExcalidrawElement | ExcalidrawElement[],
|
||||||
|
handle: TransformHandleDirection,
|
||||||
|
mouseMove: [deltaX: number, deltaY: number],
|
||||||
|
keyboardModifiers: KeyboardModifiers = {},
|
||||||
|
) {
|
||||||
|
return transform(element, handle, mouseMove, keyboardModifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
static rotate(
|
||||||
|
element: ExcalidrawElement | ExcalidrawElement[],
|
||||||
|
mouseMove: [deltaX: number, deltaY: number],
|
||||||
|
keyboardModifiers: KeyboardModifiers = {},
|
||||||
|
) {
|
||||||
|
return transform(element, "rotation", mouseMove, keyboardModifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
static group(elements: ExcalidrawElement[]) {
|
static group(elements: ExcalidrawElement[]) {
|
||||||
|
@ -16,7 +16,6 @@ import { Point } from "../types";
|
|||||||
import { KEYS } from "../keys";
|
import { KEYS } from "../keys";
|
||||||
import { LinearElementEditor } from "../element/linearElementEditor";
|
import { LinearElementEditor } from "../element/linearElementEditor";
|
||||||
import { queryByTestId, queryByText } from "@testing-library/react";
|
import { queryByTestId, queryByText } from "@testing-library/react";
|
||||||
import { resize, rotate } from "./utils";
|
|
||||||
import {
|
import {
|
||||||
getBoundTextElementPosition,
|
getBoundTextElementPosition,
|
||||||
wrapText,
|
wrapText,
|
||||||
@ -939,71 +938,10 @@ describe("Test Linear Elements", () => {
|
|||||||
expect(line.boundElements).toBeNull();
|
expect(line.boundElements).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not rotate the bound text and update position of bound text and bounding box correctly when arrow rotated", () => {
|
// TODO fix #7029 and rewrite this test
|
||||||
createThreePointerLinearElement("arrow", {
|
it.todo(
|
||||||
type: ROUNDNESS.PROPORTIONAL_RADIUS,
|
"should not rotate the bound text and update position of bound text and bounding box correctly when arrow rotated",
|
||||||
});
|
);
|
||||||
|
|
||||||
const arrow = h.elements[0] as ExcalidrawLinearElement;
|
|
||||||
|
|
||||||
const { textElement, container } = createBoundTextElement(
|
|
||||||
DEFAULT_TEXT,
|
|
||||||
arrow,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(container.angle).toBe(0);
|
|
||||||
expect(textElement.angle).toBe(0);
|
|
||||||
expect(getBoundTextElementPosition(arrow, textElement))
|
|
||||||
.toMatchInlineSnapshot(`
|
|
||||||
{
|
|
||||||
"x": 75,
|
|
||||||
"y": 60,
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
expect(textElement.text).toMatchInlineSnapshot(`
|
|
||||||
"Online whiteboard
|
|
||||||
collaboration made
|
|
||||||
easy"
|
|
||||||
`);
|
|
||||||
expect(LinearElementEditor.getElementAbsoluteCoords(container, true))
|
|
||||||
.toMatchInlineSnapshot(`
|
|
||||||
[
|
|
||||||
20,
|
|
||||||
20,
|
|
||||||
105,
|
|
||||||
80,
|
|
||||||
55.45893770831013,
|
|
||||||
45,
|
|
||||||
]
|
|
||||||
`);
|
|
||||||
|
|
||||||
rotate(container, -35, 55);
|
|
||||||
expect(container.angle).toMatchInlineSnapshot(`1.3988061968364685`);
|
|
||||||
expect(textElement.angle).toBe(0);
|
|
||||||
expect(getBoundTextElementPosition(container, textElement))
|
|
||||||
.toMatchInlineSnapshot(`
|
|
||||||
{
|
|
||||||
"x": 21.73926141863671,
|
|
||||||
"y": 73.31003398390868,
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
expect(textElement.text).toMatchInlineSnapshot(`
|
|
||||||
"Online whiteboard
|
|
||||||
collaboration made
|
|
||||||
easy"
|
|
||||||
`);
|
|
||||||
expect(LinearElementEditor.getElementAbsoluteCoords(container, true))
|
|
||||||
.toMatchInlineSnapshot(`
|
|
||||||
[
|
|
||||||
20,
|
|
||||||
20,
|
|
||||||
102.41961302274555,
|
|
||||||
86.49012635273976,
|
|
||||||
55.45893770831013,
|
|
||||||
45,
|
|
||||||
]
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should resize and position the bound text and bounding box correctly when 3 pointer arrow element resized", () => {
|
it("should resize and position the bound text and bounding box correctly when 3 pointer arrow element resized", () => {
|
||||||
createThreePointerLinearElement("arrow", {
|
createThreePointerLinearElement("arrow", {
|
||||||
@ -1042,7 +980,7 @@ describe("Test Linear Elements", () => {
|
|||||||
]
|
]
|
||||||
`);
|
`);
|
||||||
|
|
||||||
resize(container, "ne", [300, 200]);
|
UI.resize(container, "ne", [300, 200]);
|
||||||
|
|
||||||
expect({ width: container.width, height: container.height })
|
expect({ width: container.width, height: container.height })
|
||||||
.toMatchInlineSnapshot(`
|
.toMatchInlineSnapshot(`
|
||||||
|
File diff suppressed because it is too large
Load Diff
81
src/tests/rotate.test.tsx
Normal file
81
src/tests/rotate.test.tsx
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import ReactDOM from "react-dom";
|
||||||
|
import { render } from "./test-utils";
|
||||||
|
import { reseed } from "../random";
|
||||||
|
import { UI } from "./helpers/ui";
|
||||||
|
import { Excalidraw } from "../packages/excalidraw/index";
|
||||||
|
import { expect } from "vitest";
|
||||||
|
|
||||||
|
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
localStorage.clear();
|
||||||
|
reseed(7);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("unselected bound arrow updates when rotating its target element", async () => {
|
||||||
|
await render(<Excalidraw />);
|
||||||
|
const rectangle = UI.createElement("rectangle", {
|
||||||
|
width: 200,
|
||||||
|
height: 100,
|
||||||
|
});
|
||||||
|
const arrow = UI.createElement("arrow", {
|
||||||
|
x: -80,
|
||||||
|
y: 50,
|
||||||
|
width: 70,
|
||||||
|
height: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(arrow.endBinding?.elementId).toEqual(rectangle.id);
|
||||||
|
|
||||||
|
UI.rotate(rectangle, [60, 36], { shift: true });
|
||||||
|
|
||||||
|
expect(arrow.endBinding?.elementId).toEqual(rectangle.id);
|
||||||
|
expect(arrow.x).toBeCloseTo(-80);
|
||||||
|
expect(arrow.y).toBeCloseTo(50);
|
||||||
|
expect(arrow.width).toBeCloseTo(110.7, 1);
|
||||||
|
expect(arrow.height).toBeCloseTo(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("unselected bound arrows update when rotating their target elements", async () => {
|
||||||
|
await render(<Excalidraw />);
|
||||||
|
const ellipse = UI.createElement("ellipse", {
|
||||||
|
x: 0,
|
||||||
|
y: 80,
|
||||||
|
width: 300,
|
||||||
|
height: 120,
|
||||||
|
});
|
||||||
|
const ellipseArrow = UI.createElement("arrow", {
|
||||||
|
position: 0,
|
||||||
|
width: 40,
|
||||||
|
height: 80,
|
||||||
|
});
|
||||||
|
const text = UI.createElement("text", {
|
||||||
|
position: 220,
|
||||||
|
});
|
||||||
|
await UI.editText(text, "test");
|
||||||
|
const textArrow = UI.createElement("arrow", {
|
||||||
|
x: 360,
|
||||||
|
y: 300,
|
||||||
|
width: -100,
|
||||||
|
height: -40,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(ellipseArrow.endBinding?.elementId).toEqual(ellipse.id);
|
||||||
|
expect(textArrow.endBinding?.elementId).toEqual(text.id);
|
||||||
|
|
||||||
|
UI.rotate([ellipse, text], [-82, 23], { shift: true });
|
||||||
|
|
||||||
|
expect(ellipseArrow.endBinding?.elementId).toEqual(ellipse.id);
|
||||||
|
expect(ellipseArrow.x).toEqual(0);
|
||||||
|
expect(ellipseArrow.y).toEqual(0);
|
||||||
|
expect(ellipseArrow.points[0]).toEqual([0, 0]);
|
||||||
|
expect(ellipseArrow.points[1][0]).toBeCloseTo(48.5, 1);
|
||||||
|
expect(ellipseArrow.points[1][1]).toBeCloseTo(126.5, 1);
|
||||||
|
|
||||||
|
expect(textArrow.endBinding?.elementId).toEqual(text.id);
|
||||||
|
expect(textArrow.x).toEqual(360);
|
||||||
|
expect(textArrow.y).toEqual(300);
|
||||||
|
expect(textArrow.points[0]).toEqual([0, 0]);
|
||||||
|
expect(textArrow.points[1][0]).toBeCloseTo(-94, 1);
|
||||||
|
expect(textArrow.points[1][1]).toBeCloseTo(-116.1, 1);
|
||||||
|
});
|
@ -480,7 +480,7 @@ describe("tool locking & selection", () => {
|
|||||||
expect(h.state.activeTool.locked).toBe(true);
|
expect(h.state.activeTool.locked).toBe(true);
|
||||||
|
|
||||||
for (const { value } of Object.values(SHAPES)) {
|
for (const { value } of Object.values(SHAPES)) {
|
||||||
if (value !== "image" && value !== "selection") {
|
if (value !== "image" && value !== "selection" && value !== "eraser") {
|
||||||
const element = UI.createElement(value);
|
const element = UI.createElement(value);
|
||||||
expect(h.state.selectedElementIds[element.id]).not.toBe(true);
|
expect(h.state.selectedElementIds[element.id]).not.toBe(true);
|
||||||
}
|
}
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
import {
|
|
||||||
getTransformHandles,
|
|
||||||
TransformHandleDirection,
|
|
||||||
} from "../element/transformHandles";
|
|
||||||
import { ExcalidrawElement } from "../element/types";
|
|
||||||
import { Keyboard, KeyboardModifiers, Pointer } from "./helpers/ui";
|
|
||||||
|
|
||||||
const mouse = new Pointer("mouse");
|
|
||||||
const { h } = window;
|
|
||||||
|
|
||||||
export const resize = (
|
|
||||||
element: ExcalidrawElement,
|
|
||||||
handleDir: TransformHandleDirection,
|
|
||||||
mouseMove: [number, number],
|
|
||||||
keyboardModifiers: KeyboardModifiers = {},
|
|
||||||
) => {
|
|
||||||
mouse.select(element);
|
|
||||||
const handle = getTransformHandles(element, h.state.zoom, "mouse")[
|
|
||||||
handleDir
|
|
||||||
]!;
|
|
||||||
const clientX = handle[0] + handle[2] / 2;
|
|
||||||
const clientY = handle[1] + handle[3] / 2;
|
|
||||||
Keyboard.withModifierKeys(keyboardModifiers, () => {
|
|
||||||
mouse.reset();
|
|
||||||
mouse.down(clientX, clientY);
|
|
||||||
mouse.move(mouseMove[0], mouseMove[1]);
|
|
||||||
mouse.up();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const rotate = (
|
|
||||||
element: ExcalidrawElement,
|
|
||||||
deltaX: number,
|
|
||||||
deltaY: number,
|
|
||||||
keyboardModifiers: KeyboardModifiers = {},
|
|
||||||
) => {
|
|
||||||
mouse.select(element);
|
|
||||||
const handle = getTransformHandles(element, h.state.zoom, "mouse").rotation!;
|
|
||||||
const clientX = handle[0] + handle[2] / 2;
|
|
||||||
const clientY = handle[1] + handle[3] / 2;
|
|
||||||
|
|
||||||
Keyboard.withModifierKeys(keyboardModifiers, () => {
|
|
||||||
mouse.reset();
|
|
||||||
mouse.down(clientX, clientY);
|
|
||||||
mouse.move(clientX + deltaX, clientY + deltaY);
|
|
||||||
mouse.up();
|
|
||||||
});
|
|
||||||
};
|
|
Loading…
x
Reference in New Issue
Block a user