fix: frame add/remove/z-index ordering changes (#7194)

This commit is contained in:
David Luzar
2023-10-25 23:16:02 +02:00
committed by GitHub
parent f098789d16
commit 0f81c30276
5 changed files with 512 additions and 212 deletions

View File

@ -94,7 +94,7 @@ export class API {
angle?: number;
id?: string;
isDeleted?: boolean;
frameId?: ExcalidrawElement["id"];
frameId?: ExcalidrawElement["id"] | null;
groupIds?: string[];
// generic element props
strokeColor?: ExcalidrawGenericElement["strokeColor"];

View File

@ -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"]],
],
});
});
});