|
@@ -12,6 +12,11 @@ import {
|
|
|
import { AppState } from "../types";
|
|
|
import { API } from "./helpers/api";
|
|
|
import { selectGroupsForSelectedElements } from "../groups";
|
|
|
+import {
|
|
|
+ ExcalidrawElement,
|
|
|
+ ExcalidrawFrameElement,
|
|
|
+ ExcalidrawSelectionElement,
|
|
|
+} from "../element/types";
|
|
|
|
|
|
// Unmount ReactDOM from root
|
|
|
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
|
|
@@ -23,9 +28,15 @@ beforeEach(() => {
|
|
|
|
|
|
const { h } = window;
|
|
|
|
|
|
+type ExcalidrawElementType = Exclude<
|
|
|
+ ExcalidrawElement,
|
|
|
+ ExcalidrawSelectionElement
|
|
|
+>["type"];
|
|
|
+
|
|
|
const populateElements = (
|
|
|
elements: {
|
|
|
id: string;
|
|
|
+ type?: ExcalidrawElementType;
|
|
|
isDeleted?: boolean;
|
|
|
isSelected?: boolean;
|
|
|
groupIds?: string[];
|
|
@@ -34,6 +45,7 @@ const populateElements = (
|
|
|
width?: number;
|
|
|
height?: number;
|
|
|
containerId?: string;
|
|
|
+ frameId?: ExcalidrawFrameElement["id"];
|
|
|
}[],
|
|
|
appState?: Partial<AppState>,
|
|
|
) => {
|
|
@@ -50,9 +62,11 @@ const populateElements = (
|
|
|
width = 100,
|
|
|
height = 100,
|
|
|
containerId = null,
|
|
|
+ frameId = null,
|
|
|
+ type,
|
|
|
}) => {
|
|
|
const element = API.createElement({
|
|
|
- type: containerId ? "text" : "rectangle",
|
|
|
+ type: type ?? (containerId ? "text" : "rectangle"),
|
|
|
id,
|
|
|
isDeleted,
|
|
|
x,
|
|
@@ -61,6 +75,7 @@ const populateElements = (
|
|
|
height,
|
|
|
groupIds,
|
|
|
containerId,
|
|
|
+ frameId: frameId || null,
|
|
|
});
|
|
|
if (isSelected) {
|
|
|
selectedElementIds[element.id] = true;
|
|
@@ -116,6 +131,8 @@ const assertZindex = ({
|
|
|
isSelected?: true;
|
|
|
groupIds?: string[];
|
|
|
containerId?: string;
|
|
|
+ frameId?: ExcalidrawFrameElement["id"];
|
|
|
+ type?: ExcalidrawElementType;
|
|
|
}[];
|
|
|
appState?: Partial<AppState>;
|
|
|
operations: [Actions, string[]][];
|
|
@@ -1183,3 +1200,285 @@ describe("z-index manipulation", () => {
|
|
|
});
|
|
|
});
|
|
|
});
|
|
|
+
|
|
|
+describe("z-indexing with frames", () => {
|
|
|
+ beforeEach(async () => {
|
|
|
+ await render(<Excalidraw />);
|
|
|
+ });
|
|
|
+
|
|
|
+ // naming scheme:
|
|
|
+ // F# ... frame element
|
|
|
+ // F#_# ... frame child of F# (rectangle)
|
|
|
+ // R# ... unrelated element (rectangle)
|
|
|
+
|
|
|
+ it("moving whole frame by one (normalized)", () => {
|
|
|
+ // normalized frame order
|
|
|
+ assertZindex({
|
|
|
+ elements: [
|
|
|
+ { id: "F1_1", frameId: "F1" },
|
|
|
+ { id: "F1_2", frameId: "F1" },
|
|
|
+ { id: "F1", type: "frame", isSelected: true },
|
|
|
+ { id: "R1" },
|
|
|
+ { id: "R2" },
|
|
|
+ ],
|
|
|
+ operations: [
|
|
|
+ // +1
|
|
|
+ [actionBringForward, ["R1", "F1_1", "F1_2", "F1", "R2"]],
|
|
|
+ // +1
|
|
|
+ [actionBringForward, ["R1", "R2", "F1_1", "F1_2", "F1"]],
|
|
|
+ // noop
|
|
|
+ [actionBringForward, ["R1", "R2", "F1_1", "F1_2", "F1"]],
|
|
|
+ // -1
|
|
|
+ [actionSendBackward, ["R1", "F1_1", "F1_2", "F1", "R2"]],
|
|
|
+ // -1
|
|
|
+ [actionSendBackward, ["F1_1", "F1_2", "F1", "R1", "R2"]],
|
|
|
+ // noop
|
|
|
+ [actionSendBackward, ["F1_1", "F1_2", "F1", "R1", "R2"]],
|
|
|
+ ],
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ it("moving whole frame by one (DENORMALIZED)", () => {
|
|
|
+ // DENORMALIZED FRAME ORDER
|
|
|
+ assertZindex({
|
|
|
+ elements: [
|
|
|
+ { id: "F1_1", frameId: "F1" },
|
|
|
+ { id: "F1", type: "frame", isSelected: true },
|
|
|
+ { id: "F1_2", frameId: "F1" },
|
|
|
+ { id: "R1" },
|
|
|
+ { id: "R2" },
|
|
|
+ ],
|
|
|
+ operations: [
|
|
|
+ // +1
|
|
|
+ [actionBringForward, ["R1", "F1_1", "F1", "F1_2", "R2"]],
|
|
|
+ // +1
|
|
|
+ [actionBringForward, ["R1", "R2", "F1_1", "F1", "F1_2"]],
|
|
|
+ // noop
|
|
|
+ [actionBringForward, ["R1", "R2", "F1_1", "F1", "F1_2"]],
|
|
|
+ ],
|
|
|
+ });
|
|
|
+
|
|
|
+ // DENORMALIZED FRAME ORDER
|
|
|
+ assertZindex({
|
|
|
+ elements: [
|
|
|
+ { id: "F1_1", frameId: "F1" },
|
|
|
+ { id: "F1", type: "frame", isSelected: true },
|
|
|
+ { id: "R1" },
|
|
|
+ { id: "F1_2", frameId: "F1" },
|
|
|
+ { id: "R2" },
|
|
|
+ ],
|
|
|
+ operations: [
|
|
|
+ // +1
|
|
|
+ [actionBringForward, ["R1", "F1_1", "F1", "R2", "F1_2"]],
|
|
|
+ // +1
|
|
|
+ [actionBringForward, ["R1", "R2", "F1_1", "F1", "F1_2"]],
|
|
|
+ // noop
|
|
|
+ [actionBringForward, ["R1", "R2", "F1_1", "F1", "F1_2"]],
|
|
|
+ ],
|
|
|
+ });
|
|
|
+
|
|
|
+ // DENORMALIZED FRAME ORDER
|
|
|
+ assertZindex({
|
|
|
+ elements: [
|
|
|
+ { id: "F1_1", frameId: "F1" },
|
|
|
+ { id: "R1" },
|
|
|
+ { id: "F1", type: "frame", isSelected: true },
|
|
|
+ { id: "R2" },
|
|
|
+ { id: "F1_2", frameId: "F1" },
|
|
|
+ { id: "R3" },
|
|
|
+ ],
|
|
|
+ operations: [
|
|
|
+ // +1
|
|
|
+ [actionBringForward, ["R1", "F1_1", "R2", "F1", "R3", "F1_2"]],
|
|
|
+ // +1
|
|
|
+ // FIXME incorrect, should put F1_1 after R3
|
|
|
+ [actionBringForward, ["R1", "R2", "F1_1", "R3", "F1", "F1_2"]],
|
|
|
+ // +1
|
|
|
+ // FIXME should be noop from previous step after it's fixed
|
|
|
+ [actionBringForward, ["R1", "R2", "R3", "F1_1", "F1", "F1_2"]],
|
|
|
+ ],
|
|
|
+ });
|
|
|
+
|
|
|
+ // DENORMALIZED FRAME ORDER
|
|
|
+ assertZindex({
|
|
|
+ elements: [
|
|
|
+ { id: "F1_1", frameId: "F1" },
|
|
|
+ { id: "R1" },
|
|
|
+ { id: "F1", type: "frame", isSelected: true },
|
|
|
+ { id: "R2" },
|
|
|
+ { id: "F1_2", frameId: "F1" },
|
|
|
+ { id: "R3" },
|
|
|
+ ],
|
|
|
+ operations: [
|
|
|
+ // -1
|
|
|
+ [actionSendBackward, ["F1_1", "F1", "R1", "F1_2", "R2", "R3"]],
|
|
|
+ // -1
|
|
|
+ [actionSendBackward, ["F1_1", "F1", "F1_2", "R1", "R2", "R3"]],
|
|
|
+ ],
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ it("moving selected frame children by one (normalized)", () => {
|
|
|
+ // normalized frame order
|
|
|
+ assertZindex({
|
|
|
+ elements: [
|
|
|
+ { id: "F1_1", frameId: "F1", isSelected: true },
|
|
|
+ { id: "F1_2", frameId: "F1" },
|
|
|
+ { id: "F1", type: "frame" },
|
|
|
+ { id: "R1" },
|
|
|
+ ],
|
|
|
+ operations: [
|
|
|
+ // +1
|
|
|
+ [actionBringForward, ["F1_2", "F1_1", "F1", "R1"]],
|
|
|
+ // noop
|
|
|
+ [actionBringForward, ["F1_2", "F1_1", "F1", "R1"]],
|
|
|
+ ],
|
|
|
+ });
|
|
|
+
|
|
|
+ // normalized frame order, multiple frames
|
|
|
+ assertZindex({
|
|
|
+ elements: [
|
|
|
+ { id: "F1_1", frameId: "F1", isSelected: true },
|
|
|
+ { id: "F1_2", frameId: "F1" },
|
|
|
+ { id: "F1", type: "frame" },
|
|
|
+ { id: "R1" },
|
|
|
+ { id: "F2_1", frameId: "F2", isSelected: true },
|
|
|
+ { id: "F2_2", frameId: "F2" },
|
|
|
+ { id: "F2", type: "frame" },
|
|
|
+ { id: "R2" },
|
|
|
+ ],
|
|
|
+ operations: [
|
|
|
+ // +1
|
|
|
+ [
|
|
|
+ actionBringForward,
|
|
|
+ ["F1_2", "F1_1", "F1", "R1", "F2_2", "F2_1", "F2", "R2"],
|
|
|
+ ],
|
|
|
+ // noop
|
|
|
+ [
|
|
|
+ actionBringForward,
|
|
|
+ ["F1_2", "F1_1", "F1", "R1", "F2_2", "F2_1", "F2", "R2"],
|
|
|
+ ],
|
|
|
+ ],
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ it("moving selected frame children by one (DENORMALIZED)", () => {
|
|
|
+ // DENORMALIZED FRAME ORDER
|
|
|
+ assertZindex({
|
|
|
+ elements: [
|
|
|
+ { id: "F1_1", frameId: "F1", isSelected: true },
|
|
|
+ { id: "F1", type: "frame" },
|
|
|
+ { id: "F1_2", frameId: "F1" },
|
|
|
+ { id: "R1" },
|
|
|
+ ],
|
|
|
+ operations: [
|
|
|
+ // +1
|
|
|
+ // NOTE not sure what we wanna do here
|
|
|
+ [actionBringForward, ["F1", "F1_2", "F1_1", "R1"]],
|
|
|
+ // noop
|
|
|
+ [actionBringForward, ["F1", "F1_2", "F1_1", "R1"]],
|
|
|
+ // -1
|
|
|
+ [actionSendBackward, ["F1", "F1_1", "F1_2", "R1"]],
|
|
|
+ // noop
|
|
|
+ [actionSendBackward, ["F1", "F1_1", "F1_2", "R1"]],
|
|
|
+ ],
|
|
|
+ });
|
|
|
+
|
|
|
+ // DENORMALIZED FRAME ORDER
|
|
|
+ assertZindex({
|
|
|
+ elements: [
|
|
|
+ { id: "F1_1", frameId: "F1", isSelected: true },
|
|
|
+ { id: "R1" },
|
|
|
+ { id: "F1", type: "frame" },
|
|
|
+ { id: "F1_2", frameId: "F1" },
|
|
|
+ { id: "R2" },
|
|
|
+ ],
|
|
|
+ operations: [
|
|
|
+ // +1
|
|
|
+ // NOTE not sure what we wanna do here
|
|
|
+ [actionBringForward, ["R1", "F1", "F1_2", "F1_1", "R2"]],
|
|
|
+ // noop
|
|
|
+ [actionBringForward, ["R1", "F1", "F1_2", "F1_1", "R2"]],
|
|
|
+ // -1
|
|
|
+ [actionSendBackward, ["R1", "F1", "F1_1", "F1_2", "R2"]],
|
|
|
+ // noop
|
|
|
+ [actionSendBackward, ["R1", "F1", "F1_1", "F1_2", "R2"]],
|
|
|
+ ],
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ it("moving whole frame to front/end", () => {
|
|
|
+ // normalized frame order
|
|
|
+ assertZindex({
|
|
|
+ elements: [
|
|
|
+ { id: "F1_1", frameId: "F1" },
|
|
|
+ { id: "F1_2", frameId: "F1" },
|
|
|
+ { id: "F1", type: "frame", isSelected: true },
|
|
|
+ { id: "R1" },
|
|
|
+ { id: "R2" },
|
|
|
+ ],
|
|
|
+ operations: [
|
|
|
+ // +∞
|
|
|
+ [actionBringToFront, ["R1", "R2", "F1_1", "F1_2", "F1"]],
|
|
|
+ // noop
|
|
|
+ [actionBringToFront, ["R1", "R2", "F1_1", "F1_2", "F1"]],
|
|
|
+ // -∞
|
|
|
+ [actionSendToBack, ["F1_1", "F1_2", "F1", "R1", "R2"]],
|
|
|
+ // noop
|
|
|
+ [actionSendToBack, ["F1_1", "F1_2", "F1", "R1", "R2"]],
|
|
|
+ ],
|
|
|
+ });
|
|
|
+
|
|
|
+ // DENORMALIZED FRAME ORDER
|
|
|
+ assertZindex({
|
|
|
+ elements: [
|
|
|
+ { id: "F1_1", frameId: "F1" },
|
|
|
+ { id: "F1", type: "frame", isSelected: true },
|
|
|
+ { id: "F1_2", frameId: "F1" },
|
|
|
+ { id: "R1" },
|
|
|
+ { id: "R2" },
|
|
|
+ ],
|
|
|
+ operations: [
|
|
|
+ // +∞
|
|
|
+ [actionBringToFront, ["R1", "R2", "F1_1", "F1", "F1_2"]],
|
|
|
+ // noop
|
|
|
+ [actionBringToFront, ["R1", "R2", "F1_1", "F1", "F1_2"]],
|
|
|
+ // -∞
|
|
|
+ [actionSendToBack, ["F1_1", "F1", "F1_2", "R1", "R2"]],
|
|
|
+ // noop
|
|
|
+ [actionSendToBack, ["F1_1", "F1", "F1_2", "R1", "R2"]],
|
|
|
+ ],
|
|
|
+ });
|
|
|
+
|
|
|
+ // DENORMALIZED FRAME ORDER
|
|
|
+ assertZindex({
|
|
|
+ elements: [
|
|
|
+ { id: "F1_1", frameId: "F1" },
|
|
|
+ { id: "F1", type: "frame", isSelected: true },
|
|
|
+ { id: "R1" },
|
|
|
+ { id: "F1_2", frameId: "F1" },
|
|
|
+ { id: "R2" },
|
|
|
+ ],
|
|
|
+ operations: [
|
|
|
+ // +∞
|
|
|
+ [actionBringToFront, ["R1", "R2", "F1_1", "F1", "F1_2"]],
|
|
|
+ ],
|
|
|
+ });
|
|
|
+
|
|
|
+ // DENORMALIZED FRAME ORDER
|
|
|
+ assertZindex({
|
|
|
+ elements: [
|
|
|
+ { id: "F1_1", frameId: "F1" },
|
|
|
+ { id: "R1" },
|
|
|
+ { id: "F1", type: "frame", isSelected: true },
|
|
|
+ { id: "R2" },
|
|
|
+ { id: "F1_2", frameId: "F1" },
|
|
|
+ { id: "R3" },
|
|
|
+ ],
|
|
|
+ operations: [
|
|
|
+ // +1
|
|
|
+ [actionBringToFront, ["R1", "R2", "R3", "F1_1", "F1", "F1_2"]],
|
|
|
+ ],
|
|
|
+ });
|
|
|
+ });
|
|
|
+});
|