mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
build: decouple package deps and introduce yarn workspaces (#7415)
* feat: decouple package deps and introduce yarn workspaces * update root directory * fix * fix scripts * fix lint * update path in scripts * remove yarn.lock files from packages * ignore workspace * dummy * dummy * remove comment check * revert workflow changes * ignore ws when installing gh actions * remove log * update path * fix * fix typo
This commit is contained in:
parent
b7d7ccc929
commit
d6cd8b78f1
567 changed files with 5066 additions and 8648 deletions
410
packages/excalidraw/tests/scene/export.test.ts
Normal file
410
packages/excalidraw/tests/scene/export.test.ts
Normal file
|
@ -0,0 +1,410 @@
|
|||
import { NonDeletedExcalidrawElement } from "../../element/types";
|
||||
import * as exportUtils from "../../scene/export";
|
||||
import {
|
||||
diamondFixture,
|
||||
ellipseFixture,
|
||||
rectangleWithLinkFixture,
|
||||
} from "../fixtures/elementFixture";
|
||||
import { API } from "../helpers/api";
|
||||
import { exportToCanvas, exportToSvg } from "../../../utils";
|
||||
import { FRAME_STYLE } from "../../constants";
|
||||
import { prepareElementsForExport } from "../../data";
|
||||
|
||||
describe("exportToSvg", () => {
|
||||
window.EXCALIDRAW_ASSET_PATH = "/";
|
||||
const ELEMENT_HEIGHT = 100;
|
||||
const ELEMENT_WIDTH = 100;
|
||||
const ELEMENTS = [
|
||||
{ ...diamondFixture, height: ELEMENT_HEIGHT, width: ELEMENT_WIDTH },
|
||||
{ ...ellipseFixture, height: ELEMENT_HEIGHT, width: ELEMENT_WIDTH },
|
||||
] as NonDeletedExcalidrawElement[];
|
||||
|
||||
const DEFAULT_OPTIONS = {
|
||||
exportBackground: false,
|
||||
viewBackgroundColor: "#ffffff",
|
||||
files: {},
|
||||
};
|
||||
|
||||
it("with default arguments", async () => {
|
||||
const svgElement = await exportUtils.exportToSvg(
|
||||
ELEMENTS,
|
||||
DEFAULT_OPTIONS,
|
||||
null,
|
||||
);
|
||||
|
||||
expect(svgElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("with background color", async () => {
|
||||
const BACKGROUND_COLOR = "#abcdef";
|
||||
|
||||
const svgElement = await exportUtils.exportToSvg(
|
||||
ELEMENTS,
|
||||
{
|
||||
...DEFAULT_OPTIONS,
|
||||
exportBackground: true,
|
||||
viewBackgroundColor: BACKGROUND_COLOR,
|
||||
},
|
||||
null,
|
||||
);
|
||||
|
||||
expect(svgElement.querySelector("rect")).toHaveAttribute(
|
||||
"fill",
|
||||
BACKGROUND_COLOR,
|
||||
);
|
||||
});
|
||||
|
||||
it("with dark mode", async () => {
|
||||
const svgElement = await exportUtils.exportToSvg(
|
||||
ELEMENTS,
|
||||
{
|
||||
...DEFAULT_OPTIONS,
|
||||
exportWithDarkMode: true,
|
||||
},
|
||||
null,
|
||||
);
|
||||
|
||||
expect(svgElement.getAttribute("filter")).toMatchInlineSnapshot(
|
||||
'"_themeFilter_1883f3"',
|
||||
);
|
||||
});
|
||||
|
||||
it("with exportPadding", async () => {
|
||||
const svgElement = await exportUtils.exportToSvg(
|
||||
ELEMENTS,
|
||||
{
|
||||
...DEFAULT_OPTIONS,
|
||||
exportPadding: 0,
|
||||
},
|
||||
null,
|
||||
);
|
||||
|
||||
expect(svgElement).toHaveAttribute("height", ELEMENT_HEIGHT.toString());
|
||||
expect(svgElement).toHaveAttribute("width", ELEMENT_WIDTH.toString());
|
||||
expect(svgElement).toHaveAttribute(
|
||||
"viewBox",
|
||||
`0 0 ${ELEMENT_WIDTH} ${ELEMENT_HEIGHT}`,
|
||||
);
|
||||
});
|
||||
|
||||
it("with scale", async () => {
|
||||
const SCALE = 2;
|
||||
|
||||
const svgElement = await exportUtils.exportToSvg(
|
||||
ELEMENTS,
|
||||
{
|
||||
...DEFAULT_OPTIONS,
|
||||
exportPadding: 0,
|
||||
exportScale: SCALE,
|
||||
},
|
||||
null,
|
||||
);
|
||||
|
||||
expect(svgElement).toHaveAttribute(
|
||||
"height",
|
||||
(ELEMENT_HEIGHT * SCALE).toString(),
|
||||
);
|
||||
expect(svgElement).toHaveAttribute(
|
||||
"width",
|
||||
(ELEMENT_WIDTH * SCALE).toString(),
|
||||
);
|
||||
});
|
||||
|
||||
it("with exportEmbedScene", async () => {
|
||||
const svgElement = await exportUtils.exportToSvg(
|
||||
ELEMENTS,
|
||||
{
|
||||
...DEFAULT_OPTIONS,
|
||||
exportEmbedScene: true,
|
||||
},
|
||||
null,
|
||||
);
|
||||
expect(svgElement.innerHTML).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("with elements that have a link", async () => {
|
||||
const svgElement = await exportUtils.exportToSvg(
|
||||
[rectangleWithLinkFixture],
|
||||
DEFAULT_OPTIONS,
|
||||
null,
|
||||
);
|
||||
expect(svgElement.innerHTML).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe("exporting frames", () => {
|
||||
const getFrameNameHeight = (exportType: "canvas" | "svg") => {
|
||||
const height =
|
||||
FRAME_STYLE.nameFontSize * FRAME_STYLE.nameLineHeight +
|
||||
FRAME_STYLE.nameOffsetY;
|
||||
// canvas truncates dimensions to integers
|
||||
if (exportType === "canvas") {
|
||||
return Math.trunc(height);
|
||||
}
|
||||
return height;
|
||||
};
|
||||
|
||||
// a few tests with exportToCanvas (where we can't inspect elements)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe("exportToCanvas", () => {
|
||||
it("exporting canvas with a single frame shouldn't crop if not exporting frame directly", async () => {
|
||||
const elements = [
|
||||
API.createElement({
|
||||
type: "frame",
|
||||
width: 100,
|
||||
height: 100,
|
||||
x: 0,
|
||||
y: 0,
|
||||
}),
|
||||
API.createElement({
|
||||
type: "rectangle",
|
||||
width: 100,
|
||||
height: 100,
|
||||
x: 100,
|
||||
y: 0,
|
||||
}),
|
||||
];
|
||||
|
||||
const canvas = await exportToCanvas({
|
||||
elements,
|
||||
files: null,
|
||||
exportPadding: 0,
|
||||
});
|
||||
|
||||
expect(canvas.width).toEqual(200);
|
||||
expect(canvas.height).toEqual(100 + getFrameNameHeight("canvas"));
|
||||
});
|
||||
|
||||
it("exporting canvas with a single frame should crop when exporting frame directly", async () => {
|
||||
const frame = API.createElement({
|
||||
type: "frame",
|
||||
width: 100,
|
||||
height: 100,
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
const elements = [
|
||||
frame,
|
||||
API.createElement({
|
||||
type: "rectangle",
|
||||
width: 100,
|
||||
height: 100,
|
||||
x: 100,
|
||||
y: 0,
|
||||
}),
|
||||
];
|
||||
|
||||
const canvas = await exportToCanvas({
|
||||
elements,
|
||||
files: null,
|
||||
exportPadding: 0,
|
||||
exportingFrame: frame,
|
||||
});
|
||||
|
||||
expect(canvas.width).toEqual(frame.width);
|
||||
expect(canvas.height).toEqual(frame.height);
|
||||
});
|
||||
});
|
||||
|
||||
// exportToSvg (so we can test for element existence)
|
||||
// ---------------------------------------------------------------------------
|
||||
describe("exportToSvg", () => {
|
||||
it("exporting frame should include overlapping elements, but crop to frame", async () => {
|
||||
const frame = API.createElement({
|
||||
type: "frame",
|
||||
width: 100,
|
||||
height: 100,
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
const frameChild = API.createElement({
|
||||
type: "rectangle",
|
||||
width: 100,
|
||||
height: 100,
|
||||
x: 0,
|
||||
y: 50,
|
||||
frameId: frame.id,
|
||||
});
|
||||
const rectOverlapping = API.createElement({
|
||||
type: "rectangle",
|
||||
width: 100,
|
||||
height: 100,
|
||||
x: 50,
|
||||
y: 0,
|
||||
});
|
||||
|
||||
const svg = await exportToSvg({
|
||||
elements: [rectOverlapping, frame, frameChild],
|
||||
files: null,
|
||||
exportPadding: 0,
|
||||
exportingFrame: frame,
|
||||
});
|
||||
|
||||
// frame itself isn't exported
|
||||
expect(svg.querySelector(`[data-id="${frame.id}"]`)).toBeNull();
|
||||
// frame child is exported
|
||||
expect(svg.querySelector(`[data-id="${frameChild.id}"]`)).not.toBeNull();
|
||||
// overlapping element is exported
|
||||
expect(
|
||||
svg.querySelector(`[data-id="${rectOverlapping.id}"]`),
|
||||
).not.toBeNull();
|
||||
|
||||
expect(svg.getAttribute("width")).toBe(frame.width.toString());
|
||||
expect(svg.getAttribute("height")).toBe(frame.height.toString());
|
||||
});
|
||||
|
||||
it("should filter non-overlapping elements when exporting a frame", async () => {
|
||||
const frame = API.createElement({
|
||||
type: "frame",
|
||||
width: 100,
|
||||
height: 100,
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
const frameChild = API.createElement({
|
||||
type: "rectangle",
|
||||
width: 100,
|
||||
height: 100,
|
||||
x: 0,
|
||||
y: 50,
|
||||
frameId: frame.id,
|
||||
});
|
||||
const elementOutside = API.createElement({
|
||||
type: "rectangle",
|
||||
width: 100,
|
||||
height: 100,
|
||||
x: 200,
|
||||
y: 0,
|
||||
});
|
||||
|
||||
const svg = await exportToSvg({
|
||||
elements: [frameChild, frame, elementOutside],
|
||||
files: null,
|
||||
exportPadding: 0,
|
||||
exportingFrame: frame,
|
||||
});
|
||||
|
||||
// frame itself isn't exported
|
||||
expect(svg.querySelector(`[data-id="${frame.id}"]`)).toBeNull();
|
||||
// frame child is exported
|
||||
expect(svg.querySelector(`[data-id="${frameChild.id}"]`)).not.toBeNull();
|
||||
// non-overlapping element is not exported
|
||||
expect(svg.querySelector(`[data-id="${elementOutside.id}"]`)).toBeNull();
|
||||
|
||||
expect(svg.getAttribute("width")).toBe(frame.width.toString());
|
||||
expect(svg.getAttribute("height")).toBe(frame.height.toString());
|
||||
});
|
||||
|
||||
it("should export multiple frames when selected, excluding overlapping elements", async () => {
|
||||
const frame1 = API.createElement({
|
||||
type: "frame",
|
||||
width: 100,
|
||||
height: 100,
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
const frame2 = API.createElement({
|
||||
type: "frame",
|
||||
width: 100,
|
||||
height: 100,
|
||||
x: 200,
|
||||
y: 0,
|
||||
});
|
||||
|
||||
const frame1Child = API.createElement({
|
||||
type: "rectangle",
|
||||
width: 100,
|
||||
height: 100,
|
||||
x: 0,
|
||||
y: 50,
|
||||
frameId: frame1.id,
|
||||
});
|
||||
const frame2Child = API.createElement({
|
||||
type: "rectangle",
|
||||
width: 100,
|
||||
height: 100,
|
||||
x: 200,
|
||||
y: 0,
|
||||
frameId: frame2.id,
|
||||
});
|
||||
const frame2Overlapping = API.createElement({
|
||||
type: "rectangle",
|
||||
width: 100,
|
||||
height: 100,
|
||||
x: 350,
|
||||
y: 0,
|
||||
});
|
||||
|
||||
// low-level exportToSvg api expects elements to be pre-filtered, so let's
|
||||
// use the filter we use in the editor
|
||||
const { exportedElements, exportingFrame } = prepareElementsForExport(
|
||||
[frame1Child, frame1, frame2Child, frame2, frame2Overlapping],
|
||||
{
|
||||
selectedElementIds: { [frame1.id]: true, [frame2.id]: true },
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
const svg = await exportToSvg({
|
||||
elements: exportedElements,
|
||||
files: null,
|
||||
exportPadding: 0,
|
||||
exportingFrame,
|
||||
});
|
||||
|
||||
// frames themselves should be exported when multiple frames selected
|
||||
expect(svg.querySelector(`[data-id="${frame1.id}"]`)).not.toBeNull();
|
||||
expect(svg.querySelector(`[data-id="${frame2.id}"]`)).not.toBeNull();
|
||||
// children should be epxorted
|
||||
expect(svg.querySelector(`[data-id="${frame1Child.id}"]`)).not.toBeNull();
|
||||
expect(svg.querySelector(`[data-id="${frame2Child.id}"]`)).not.toBeNull();
|
||||
// overlapping elements or non-overlapping elements should not be exported
|
||||
expect(
|
||||
svg.querySelector(`[data-id="${frame2Overlapping.id}"]`),
|
||||
).toBeNull();
|
||||
|
||||
expect(svg.getAttribute("width")).toBe(
|
||||
(frame2.x + frame2.width).toString(),
|
||||
);
|
||||
expect(svg.getAttribute("height")).toBe(
|
||||
(frame2.y + frame2.height + getFrameNameHeight("svg")).toString(),
|
||||
);
|
||||
});
|
||||
|
||||
it("should render frame alone when not selected", async () => {
|
||||
const frame = API.createElement({
|
||||
type: "frame",
|
||||
width: 100,
|
||||
height: 100,
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
|
||||
// low-level exportToSvg api expects elements to be pre-filtered, so let's
|
||||
// use the filter we use in the editor
|
||||
const { exportedElements, exportingFrame } = prepareElementsForExport(
|
||||
[frame],
|
||||
{
|
||||
selectedElementIds: {},
|
||||
},
|
||||
false,
|
||||
);
|
||||
|
||||
const svg = await exportToSvg({
|
||||
elements: exportedElements,
|
||||
files: null,
|
||||
exportPadding: 0,
|
||||
exportingFrame,
|
||||
});
|
||||
|
||||
// frame itself isn't exported
|
||||
expect(svg.querySelector(`[data-id="${frame.id}"]`)).not.toBeNull();
|
||||
|
||||
expect(svg.getAttribute("width")).toBe(frame.width.toString());
|
||||
expect(svg.getAttribute("height")).toBe(
|
||||
(frame.height + getFrameNameHeight("svg")).toString(),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue