ronantakizawa/codereview-bench huggingface dataset:
Repository: excalidraw/excalidraw
File: src/tests/viewMode.test.tsx (TypeScript)

Code before:
import React from "react";
import { render, GlobalTestState } from "./test-utils";
import ExcalidrawApp from "../excalidraw-app";
import { KEYS } from "../keys";
import { Keyboard, Pointer, UI } from "./helpers/ui";
import { CURSOR_TYPE } from "../constants";

const { h } = window;
const mouse = new Pointer("mouse");
const touch = new Pointer("touch");
const pen = new Pointer("pen");
const pointerTypes = [mouse, touch, pen];

describe("view mode", () => {
  beforeEach(async () => {
    await render(<ExcalidrawApp />);
  });

  it("after switching to view mode – cursor type should be pointer", async () => {
    h.setState({ viewModeEnabled: true });
    expect(GlobalTestState.canvas.style._values.cursor).toBe(
      CURSOR_TYPE.GRABBING,
    );
  });

  it("after switching to view mode, moving, clicking, and pressing space key – cursor type should be pointer", async () => {

Review comment (suggestion):
lets these tests in `App.test.tsx` instead so its easy to relate the test files with source files.

Code after:
import React from "react";
import { render, GlobalTestState } from "./test-utils";
import ExcalidrawApp from "../excalidraw-app";
import { KEYS } from "../keys";
import { Keyboard, Pointer, UI } from "./helpers/ui";
import { CURSOR_TYPE } from "../constants";

const { h } = window;
const mouse = new Pointer("mouse");
const touch = new Pointer("touch");
const pen = new Pointer("pen");
const pointerTypes = [mouse, touch, pen];

describe("view mode", () => {
  beforeEach(async () => {
    await render(<ExcalidrawApp />);
  });

  it("after switching to view mode – cursor type should be pointer", async () => {
    h.setState({ viewModeEnabled: true });
    expect(GlobalTestState.canvas.style.cursor).toBe(CURSOR_TYPE.GRAB);
  });

  it("after switching to view mode, moving, clicking, and pressing space key – cursor type should be pointer", async () => {
    h.setState({ viewModeEnabled: true });
Repository: excalidraw/excalidraw
File: src/tests/zindex.test.tsx (TypeScript)

Code before:
    isDeleted?: boolean;
    isSelected?: boolean;
    groupIds?: string[];
    y?: number;
    x?: number;
    width?: number;
    height?: number;
    containerId?: string;
  }[],
) => {
  const selectedElementIds: any = {};

  const newElements = elements.map(
    ({
      id,
      isDeleted = false,
      isSelected = false,
      groupIds = [],
      y = 100,
      x = 100,
      width = 100,
      height = 100,
      containerId = null,
    }) => {
      const element = API.createElement({
        type: containerId != null ? "text" : "rectangle",
        id,
        isDeleted,
        x,
        y,
        width,
        height,
        groupIds,
        containerId,
      });
      if (isSelected) {
        selectedElementIds[element.id] = true;
      }
      return element;
    },
  );

  // initialize `boundElements` on containers, if applicable
  h.elements = newElements.map((element, index, elements) => {
    const nextElement = elements[index + 1];
    if (
      nextElement &&
      "containerId" in nextElement &&
      element.id === nextElement.containerId
    ) {
      return {

Review comment (suggestion):
```suggestion
        type: containerId ? "text" : "rectangle",
```

Code after:
    isDeleted?: boolean;
    isSelected?: boolean;
    groupIds?: string[];
    y?: number;
    x?: number;
    width?: number;
    height?: number;
    containerId?: string;
  }[],
) => {
  const selectedElementIds: any = {};

  const newElements = elements.map(
    ({
      id,
      isDeleted = false,
      isSelected = false,
      groupIds = [],
      y = 100,
      x = 100,
      width = 100,
      height = 100,
      containerId = null,
    }) => {
      const element = API.createElement({
        type: containerId ? "text" : "rectangle",
        id,
        isDeleted,
        x,
        y,
        width,
        height,
        groupIds,
        containerId,
      });
      if (isSelected) {
        selectedElementIds[element.id] = true;
      }
      return element;
    },
  );

  // initialize `boundElements` on containers, if applicable
  h.elements = newElements.map((element, index, elements) => {
    const nextElement = elements[index + 1];
    if (
      nextElement &&
      "containerId" in nextElement &&
      element.id === nextElement.containerId
    ) {
      return {
Repository: excalidraw/excalidraw
File: src/excalidraw-app/index.tsx (TypeScript)

Code before:
    initialStatePromiseRef.current.promise =
      resolvablePromise<ImportedDataState | null>();
  }

  const saveDebounced = debounce(
    async (
      elements: readonly ExcalidrawElement[],
      appState: AppState,
      files: BinaryFiles,
      onFilesSaved: () => void,
    ) => {
      saveToLocalStorage(elements, appState);

      await localFileStorage.saveFiles({
        elements,
        files,
      });
      onFilesSaved();
    },
    SAVE_TO_LOCAL_STORAGE_TIMEOUT,
  );

  const onBlur = useCallback(() => {
    saveDebounced.flush();
  }, [saveDebounced]);

  useEffect(() => {
    // Delayed so that the app has a time to load the latest SW
    setTimeout(() => {
      trackEvent("load", "version", getVersion());
    }, VERSION_TIMEOUT);
  }, []);

  const [excalidrawAPI, excalidrawRefCallback] =
    useCallbackRefState<ExcalidrawImperativeAPI>();

  const collabAPI = useContext(CollabContext)?.api;

  useEffect(() => {
    if (!collabAPI || !excalidrawAPI) {
      return;
    }

    const loadImages = (
      data: ResolutionType<typeof initializeScene>,
      isInitialLoad = false,
    ) => {
      if (!data.scene) {
        return;
      }
      if (collabAPI.isCollaborating()) {

Review comment (suggestion):
not sure if you moved this inside the component for some reason that's been removed in subsequent commits, but since this isn't needed I'm moving it out again

Code after:
      href="https://plus.excalidraw.com/plus?utm_source=excalidraw&utm_medium=banner&utm_campaign=launch"
      target="_blank"
      rel="noreferrer"
    >
      Try out now!
    </a>
  </p>
);

const ExcalidrawWrapper = () => {
  const [errorMessage, setErrorMessage] = useState("");
  let currentLangCode = languageDetector.detect() || defaultLang.code;
  if (Array.isArray(currentLangCode)) {
    currentLangCode = currentLangCode[0];
  }
  const [langCode, setLangCode] = useState(currentLangCode);
  // initial state
  // ---------------------------------------------------------------------------

  const initialStatePromiseRef = useRef<{
    promise: ResolvablePromise<ImportedDataState | null>;
  }>({ promise: null! });
  if (!initialStatePromiseRef.current.promise) {
    initialStatePromiseRef.current.promise =
      resolvablePromise<ImportedDataState | null>();
  }

  useEffect(() => {
    // Delayed so that the app has a time to load the latest SW
    setTimeout(() => {
      trackEvent("load", "version", getVersion());
    }, VERSION_TIMEOUT);
  }, []);

  const [excalidrawAPI, excalidrawRefCallback] =
    useCallbackRefState<ExcalidrawImperativeAPI>();

  const collabAPI = useContext(CollabContext)?.api;

  useEffect(() => {
    if (!collabAPI || !excalidrawAPI) {
      return;
    }

    const loadImages = (
      data: ResolutionType<typeof initializeScene>,
      isInitialLoad = false,
    ) => {
      if (!data.scene) {
        return;
      }
Repository: excalidraw/excalidraw
File: src/element/textWysiwyg.test.tsx (TypeScript)

Code before:
        shiftKey: true,
      });
      // cursor: "Line#1\n  Line#|2"
      textarea.value = `Line#1\n  Line#2`;
      textarea.selectionStart = 15;
      textarea.selectionEnd = 15;
      textarea.dispatchEvent(event);

      expect(textarea.value).toEqual(`Line#1\nLine#2`);
    });

    it("should remove nothing", () => {
      const event = new KeyboardEvent("keydown", {
        key: KEYS.TAB,
        shiftKey: true,
      });
      // cursor: "Line#1\n  Li|ne#2"
      textarea.value = `Line#1\nLine#2`;
      textarea.selectionStart = 9;
      textarea.selectionEnd = 9;
      textarea.dispatchEvent(event);

      expect(textarea.value).toEqual(`Line#1\nLine#2`);
    });

    it("resizing text via shortcuts while in wysiwyg", () => {
      textarea.value = "abc def";
      const origFontSize = textElement.fontSize;
      textarea.dispatchEvent(
        new KeyboardEvent("keydown", {
          key: KEYS.CHEVRON_RIGHT,
          ctrlKey: true,
          shiftKey: true,
        }),
      );
      expect(textElement.fontSize).toBe(origFontSize * 1.1);

      textarea.dispatchEvent(
        new KeyboardEvent("keydown", {
          key: KEYS.CHEVRON_LEFT,
          ctrlKey: true,
          shiftKey: true,
        }),
      );
      expect(textElement.fontSize).toBe(origFontSize);
    });
  });

  describe("Test bounded text", () => {
    let rectangle: any;
    const {

Review comment (suggestion):
```suggestion
    it("should resize text via shortcuts while in wysiwyg", () => {
```

Code after:
        shiftKey: true,
      });
      // cursor: "Line#1\n  Line#|2"
      textarea.value = `Line#1\n  Line#2`;
      textarea.selectionStart = 15;
      textarea.selectionEnd = 15;
      textarea.dispatchEvent(event);

      expect(textarea.value).toEqual(`Line#1\nLine#2`);
    });

    it("should remove nothing", () => {
      const event = new KeyboardEvent("keydown", {
        key: KEYS.TAB,
        shiftKey: true,
      });
      // cursor: "Line#1\n  Li|ne#2"
      textarea.value = `Line#1\nLine#2`;
      textarea.selectionStart = 9;
      textarea.selectionEnd = 9;
      textarea.dispatchEvent(event);

      expect(textarea.value).toEqual(`Line#1\nLine#2`);
    });

    it("should resize text via shortcuts while in wysiwyg", () => {
      textarea.value = "abc def";
      const origFontSize = textElement.fontSize;
      textarea.dispatchEvent(
        new KeyboardEvent("keydown", {
          key: KEYS.CHEVRON_RIGHT,
          ctrlKey: true,
          shiftKey: true,
        }),
      );
      expect(textElement.fontSize).toBe(origFontSize * 1.1);

      textarea.dispatchEvent(
        new KeyboardEvent("keydown", {
          key: KEYS.CHEVRON_LEFT,
          ctrlKey: true,
          shiftKey: true,
        }),
      );
      expect(textElement.fontSize).toBe(origFontSize);
    });
  });

  describe("Test bounded text", () => {
    let rectangle: any;
    const {
Repository: excalidraw/excalidraw
File: src/tests/resize.test.tsx (TypeScript)

Code before:
      expect([element.x, element.y]).toEqual(topLeft);
    },
  );
});

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();
  });
};

describe("text element", () => {
  it("changing fontSize via keyboard", async () => {
    await render(<ExcalidrawApp />);

    const textElement = API.createElement({
      type: "text",
      text: "abc",
    });

    window.h.elements = [textElement];

    API.setSelectedElements([textElement]);

    const origFontSize = textElement.fontSize;

    Keyboard.withModifierKeys({ shift: true, ctrl: true }, () => {
      Keyboard.keyDown(KEYS.CHEVRON_RIGHT);
      expect((window.h.elements[0] as ExcalidrawTextElement).fontSize).toBe(
        origFontSize * 1.1,
      );
      Keyboard.keyDown(KEYS.CHEVRON_LEFT);
      expect((window.h.elements[0] as ExcalidrawTextElement).fontSize).toBe(
        origFontSize,
      );
    });
  });

Review comment (suggestion):
```suggestion
describe("Test text element", () => {
```

Code after:
      expect([element.x, element.y]).toEqual(topLeft);
    },
  );
});

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();
  });
};

describe("Test text element", () => {
  it("should update font size via keyboard", async () => {
    await render(<ExcalidrawApp />);

    const textElement = API.createElement({
      type: "text",
      text: "abc",
    });

    window.h.elements = [textElement];

    API.setSelectedElements([textElement]);

    const origFontSize = textElement.fontSize;

    Keyboard.withModifierKeys({ shift: true, ctrl: true }, () => {
      Keyboard.keyDown(KEYS.CHEVRON_RIGHT);
      expect((window.h.elements[0] as ExcalidrawTextElement).fontSize).toBe(
        origFontSize * 1.1,
      );
      Keyboard.keyDown(KEYS.CHEVRON_LEFT);
      expect((window.h.elements[0] as ExcalidrawTextElement).fontSize).toBe(
        origFontSize,
      );
    });
  });
Repository: excalidraw/excalidraw
File: src/tests/resize.test.tsx (TypeScript)

Code before:
    },
  );
});

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();
  });
};

describe("text element", () => {
  it("changing fontSize via keyboard", async () => {
    await render(<ExcalidrawApp />);

    const textElement = API.createElement({
      type: "text",
      text: "abc",
    });

    window.h.elements = [textElement];

    API.setSelectedElements([textElement]);

    const origFontSize = textElement.fontSize;

    Keyboard.withModifierKeys({ shift: true, ctrl: true }, () => {
      Keyboard.keyDown(KEYS.CHEVRON_RIGHT);
      expect((window.h.elements[0] as ExcalidrawTextElement).fontSize).toBe(
        origFontSize * 1.1,
      );
      Keyboard.keyDown(KEYS.CHEVRON_LEFT);
      expect((window.h.elements[0] as ExcalidrawTextElement).fontSize).toBe(
        origFontSize,
      );
    });
  });
});

Review comment (suggestion):
```suggestion
  it("should update font size via keyboard", async () => {
```

Code after:
    },
  );
});

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();
  });
};

describe("Test text element", () => {
  it("should update font size via keyboard", async () => {
    await render(<ExcalidrawApp />);

    const textElement = API.createElement({
      type: "text",
      text: "abc",
    });

    window.h.elements = [textElement];

    API.setSelectedElements([textElement]);

    const origFontSize = textElement.fontSize;

    Keyboard.withModifierKeys({ shift: true, ctrl: true }, () => {
      Keyboard.keyDown(KEYS.CHEVRON_RIGHT);
      expect((window.h.elements[0] as ExcalidrawTextElement).fontSize).toBe(
        origFontSize * 1.1,
      );
      Keyboard.keyDown(KEYS.CHEVRON_LEFT);
      expect((window.h.elements[0] as ExcalidrawTextElement).fontSize).toBe(
        origFontSize,
      );
    });
  });
});
Repository: excalidraw/excalidraw
File: src/components/App.tsx (TypeScript)

Code before:
        pointerDownState,
      );
    } else if (this.state.elementType === "image") {
      // reset image preview on pointerdown
      setCursor(this.canvas, CURSOR_TYPE.CROSSHAIR);

      if (!this.state.pendingImageElement) {
        return;
      }

      this.setState({
        draggingElement: this.state.pendingImageElement,
        editingElement: this.state.pendingImageElement,
        pendingImageElement: null,
        multiElement: null,
      });

      const { x, y } = viewportCoordsToSceneCoords(event, this.state);
      mutateElement(this.state.pendingImageElement, {
        x,
        y,
      });
    } else if (this.state.elementType === "freedraw") {
      if (
        (this.state.penLocked && event.pointerType === "pen") ||
        !this.state.penLocked
      ) {
        this.handleFreeDrawElementOnPointerDown(
          event,
          this.state.elementType,
          pointerDownState,
        );
      }
    } else {
      this.createGenericElementOnPointerDown(
        this.state.elementType,
        pointerDownState,
      );
    }

    const onPointerMove =
      this.onPointerMoveFromPointerDownHandler(pointerDownState);

    const onPointerUp =
      this.onPointerUpFromPointerDownHandler(pointerDownState);

    const onKeyDown = this.onKeyDownFromPointerDownHandler(pointerDownState);
    const onKeyUp = this.onKeyUpFromPointerDownHandler(pointerDownState);

    lastPointerUp = onPointerUp;


Review comment (suggestion):
```suggestion
        (!this.state.penLocked || event.pointerType !== "touch")
```

Code after:

    const allowOnPointerDown =
      !this.state.penMode ||
      event.pointerType !== "touch" ||
      this.state.elementType === "selection" ||
      this.state.elementType === "text" ||
      this.state.elementType === "image";

    if (!allowOnPointerDown) {
      return;
    }

    if (this.state.elementType === "text") {
      this.handleTextOnPointerDown(event, pointerDownState);
      return;
    } else if (
      this.state.elementType === "arrow" ||
      this.state.elementType === "line"
    ) {
      this.handleLinearElementOnPointerDown(
        event,
        this.state.elementType,
        pointerDownState,
      );
    } else if (this.state.elementType === "image") {
      // reset image preview on pointerdown
      setCursor(this.canvas, CURSOR_TYPE.CROSSHAIR);

      if (!this.state.pendingImageElement) {
        return;
      }

      this.setState({
        draggingElement: this.state.pendingImageElement,
        editingElement: this.state.pendingImageElement,
        pendingImageElement: null,
        multiElement: null,
      });

      const { x, y } = viewportCoordsToSceneCoords(event, this.state);
      mutateElement(this.state.pendingImageElement, {
        x,
        y,
      });
    } else if (this.state.elementType === "freedraw") {
      this.handleFreeDrawElementOnPointerDown(
        event,
        this.state.elementType,
        pointerDownState,
      );
    } else {
Repository: excalidraw/excalidraw
File: src/actions/actionUnbindText.tsx (TypeScript)

Code before:
import { getNonDeletedElements, isTextElement } from "../element";
import { mutateElement } from "../element/mutateElement";
import { getBoundTextElement } from "../element/textElement";
import {
  ExcalidrawLinearElement,
  ExcalidrawTextElement,
} from "../element/types";
import { getSelectedElements } from "../scene";
import { register } from "./register";

export const actionUnbindText = register({
  name: "unbindText",
  contextItemLabel: "labels.unbindText",
  perform: (elements, appState) => {
    const selectedElements = getSelectedElements(
      getNonDeletedElements(elements),
      appState,
    );
    selectedElements.forEach((element) => {
      const boundTextElement = getBoundTextElement(element);
      if (boundTextElement) {
        mutateElement(boundTextElement as ExcalidrawTextElement, {
          containerId: null,
        });
        mutateElement(element, {
          boundElements: element.boundElements?.filter(
            (ele) =>
              !isTextElement(
                ele as ExcalidrawTextElement | ExcalidrawLinearElement,
              ),
          ),
        });
      }
    });
    return {
      elements,
      appState,
      commitToHistory: true,
    };
  },
});


Review comment (suggestion):
this type assertion (and the one below) should be unnecessary

Code after:
import { getNonDeletedElements } from "../element";
import { mutateElement } from "../element/mutateElement";
import { getBoundTextElement, measureText } from "../element/textElement";
import { ExcalidrawTextElement } from "../element/types";
import { getSelectedElements } from "../scene";
import { getFontString } from "../utils";
import { register } from "./register";

export const actionUnbindText = register({
  name: "unbindText",
  contextItemLabel: "labels.unbindText",
  perform: (elements, appState) => {
    const selectedElements = getSelectedElements(
      getNonDeletedElements(elements),
      appState,
    );
    selectedElements.forEach((element) => {
      const boundTextElement = getBoundTextElement(element);
      if (boundTextElement) {
        const { width, height, baseline } = measureText(
          boundTextElement.originalText,
          getFontString(boundTextElement),
        );
        mutateElement(boundTextElement as ExcalidrawTextElement, {
          containerId: null,
          width,
          height,
          baseline,
          text: boundTextElement.originalText,
        });
        mutateElement(element, {
          boundElements: element.boundElements?.filter(
            (ele) => ele.id !== boundTextElement.id,
          ),
        });
      }
    });
    return {
      elements,
      appState,
      commitToHistory: true,
    };
  },
});