Compare commits

..

2 Commits

Author SHA1 Message Date
dwelle
fa9631617f wip: get exact bounding box from bitmap when exporting w/o padding 2022-11-03 18:34:17 +01:00
dwelle
0314e81396 feat: support toggling export padding 2022-11-03 17:31:41 +01:00
25 changed files with 504 additions and 1896 deletions

View File

@@ -89,6 +89,28 @@ export const actionChangeExportScale = register({
},
});
export const actionChangeExportPadding = register({
name: "changeExportPadding",
trackEvent: { category: "export", action: "togglePadding" },
perform: (_elements, appState, value) => {
return {
appState: {
...appState,
exportPadding: value ? DEFAULT_EXPORT_PADDING : 0,
},
commitToHistory: false,
};
},
PanelComponent: ({ appState, updateData }) => (
<CheckboxItem
checked={!!appState.exportPadding}
onChange={(checked) => updateData(checked)}
>
{"Padding"}
</CheckboxItem>
),
});
export const actionChangeExportBackground = register({
name: "changeExportBackground",
trackEvent: { category: "export", action: "toggleBackground" },

View File

@@ -68,6 +68,7 @@ export type ActionName =
| "finalize"
| "changeProjectName"
| "changeExportBackground"
| "changeExportPadding"
| "changeExportEmbedScene"
| "changeExportScale"
| "saveToActiveFile"

View File

@@ -1,5 +1,6 @@
import oc from "open-color";
import {
DEFAULT_EXPORT_PADDING,
DEFAULT_FONT_FAMILY,
DEFAULT_FONT_SIZE,
DEFAULT_TEXT_ALIGN,
@@ -55,6 +56,7 @@ export const getDefaultAppState = (): Omit<
exportScale: defaultExportScale,
exportEmbedScene: false,
exportWithDarkMode: false,
exportPadding: DEFAULT_EXPORT_PADDING,
fileHandle: null,
gridSize: null,
isBindingEnabled: true,
@@ -145,6 +147,7 @@ const APP_STATE_STORAGE_CONF = (<
exportBackground: { browser: true, export: false, server: false },
exportEmbedScene: { browser: true, export: false, server: false },
exportScale: { browser: true, export: false, server: false },
exportPadding: { browser: true, export: false, server: false },
exportWithDarkMode: { browser: true, export: false, server: false },
fileHandle: { browser: false, export: false, server: false },
gridSize: { browser: true, export: true, server: true },

View File

@@ -516,7 +516,6 @@ class App extends React.Component<AppProps, AppState> {
const {
onCollabButtonClick,
renderTopRightUI,
renderMenuLinks,
renderFooter,
renderCustomStats,
} = this.props;
@@ -563,7 +562,6 @@ class App extends React.Component<AppProps, AppState> {
langCode={getLanguage().code}
isCollaborating={this.props.isCollaborating}
renderTopRightUI={renderTopRightUI}
renderMenuLinks={renderMenuLinks}
renderCustomFooter={renderFooter}
renderCustomStats={renderCustomStats}
renderCustomSidebar={this.props.renderSidebar}
@@ -578,7 +576,6 @@ class App extends React.Component<AppProps, AppState> {
id={this.id}
onImageAction={this.onImageAction}
renderWelcomeScreen={
this.props.hideWelcomeScreen !== true &&
this.state.showWelcomeScreen &&
this.state.activeTool.type === "selection" &&
!this.scene.getElementsIncludingDeleted().length

View File

@@ -79,7 +79,6 @@ const ImageExportModal = ({
elements,
appState,
files,
exportPadding = DEFAULT_EXPORT_PADDING,
actionManager,
onExportToPng,
onExportToSvg,
@@ -88,7 +87,6 @@ const ImageExportModal = ({
appState: AppState;
elements: readonly NonDeletedExcalidrawElement[];
files: BinaryFiles;
exportPadding?: number;
actionManager: ActionManager;
onExportToPng: ExportCB;
onExportToSvg: ExportCB;
@@ -116,7 +114,7 @@ const ImageExportModal = ({
exportToCanvas(exportedElements, appState, files, {
exportBackground,
viewBackgroundColor,
exportPadding,
exportPadding: appState.exportPadding,
})
.then((canvas) => {
// if converting to blob fails, there's some problem that will
@@ -134,7 +132,6 @@ const ImageExportModal = ({
files,
exportedElements,
exportBackground,
exportPadding,
viewBackgroundColor,
]);
@@ -151,8 +148,10 @@ const ImageExportModal = ({
// dunno why this is needed, but when the items wrap it creates
// an overflow
overflow: "hidden",
gap: ".6rem",
}}
>
{actionManager.renderAction("changeExportPadding")}
{actionManager.renderAction("changeExportBackground")}
{someElementIsSelected && (
<CheckboxItem
@@ -221,7 +220,6 @@ export const ImageExportDialog = ({
appState,
setAppState,
files,
exportPadding = DEFAULT_EXPORT_PADDING,
actionManager,
onExportToPng,
onExportToSvg,
@@ -231,7 +229,6 @@ export const ImageExportDialog = ({
setAppState: React.Component<any, AppState>["setState"];
elements: readonly NonDeletedExcalidrawElement[];
files: BinaryFiles;
exportPadding?: number;
actionManager: ActionManager;
onExportToPng: ExportCB;
onExportToSvg: ExportCB;
@@ -249,7 +246,6 @@ export const ImageExportDialog = ({
elements={elements}
appState={appState}
files={files}
exportPadding={exportPadding}
actionManager={actionManager}
onExportToPng={onExportToPng}
onExportToSvg={onExportToSvg}

View File

@@ -71,7 +71,6 @@ interface LayerUIProps {
langCode: Language["code"];
isCollaborating: boolean;
renderTopRightUI?: ExcalidrawProps["renderTopRightUI"];
renderMenuLinks?: ExcalidrawProps["renderMenuLinks"];
renderCustomFooter?: ExcalidrawProps["renderFooter"];
renderCustomStats?: ExcalidrawProps["renderCustomStats"];
renderCustomSidebar?: ExcalidrawProps["renderSidebar"];
@@ -97,7 +96,6 @@ const LayerUI = ({
showExitZenModeBtn,
isCollaborating,
renderTopRightUI,
renderMenuLinks,
renderCustomFooter,
renderCustomStats,
renderCustomSidebar,
@@ -146,6 +144,7 @@ const LayerUI = ({
exportBackground: appState.exportBackground,
name: appState.name,
viewBackgroundColor: appState.viewBackgroundColor,
exportPadding: appState.exportPadding,
},
)
.catch(muteFSAbortError)
@@ -198,7 +197,6 @@ const LayerUI = ({
})}
onClick={() => setIsMenuOpen(!isMenuOpen)}
type="button"
data-testid="menu-button"
>
{HamburgerMenuIcon}
</button>
@@ -220,19 +218,16 @@ const LayerUI = ({
actionManager.renderAction("loadScene")}
{/* // TODO barnabasmolnar/editor-redesign */}
{/* is this fine here? */}
{UIOptions.canvasActions.saveToActiveFile &&
appState.fileHandle &&
{appState.fileHandle &&
actionManager.renderAction("saveToActiveFile")}
{renderJSONExportDialog()}
{UIOptions.canvasActions.saveAsImage && (
<MenuItem
label={t("buttons.exportImage")}
icon={ExportImageIcon}
dataTestId="image-export-button"
onClick={() => setAppState({ openDialog: "imageExport" })}
shortcut={getShortcutFromShortcutName("imageExport")}
/>
)}
<MenuItem
label={t("buttons.exportImage")}
icon={ExportImageIcon}
dataTestId="image-export-button"
onClick={() => setAppState({ openDialog: "imageExport" })}
shortcut={getShortcutFromShortcutName("imageExport")}
/>
{onCollabButtonClick && (
<CollabButton
isCollaborating={isCollaborating}
@@ -243,16 +238,8 @@ const LayerUI = ({
{actionManager.renderAction("toggleShortcuts", undefined, true)}
{!appState.viewModeEnabled &&
actionManager.renderAction("clearCanvas")}
{typeof renderMenuLinks === "undefined" ? ( //zsviczian
<Separator />
) : (
renderMenuLinks && <Separator />
)}
{typeof renderMenuLinks === "undefined" ? ( //zsviczian
<MenuLinks />
) : (
renderMenuLinks && renderMenuLinks(device.isMobile, appState)
)}
<Separator />
<MenuLinks />
<Separator />
<div
style={{
@@ -262,11 +249,9 @@ const LayerUI = ({
}}
>
<div>{actionManager.renderAction("toggleTheme")}</div>
{UIOptions.showLanguageList !== false && (
<div style={{ padding: "0 0.625rem" }}>
<LanguageList style={{ width: "100%" }} />
</div>
)}
<div style={{ padding: "0 0.625rem" }}>
<LanguageList style={{ width: "100%" }} />
</div>
{!appState.viewModeEnabled && (
<div>
<div style={{ fontSize: ".75rem", marginBottom: ".5rem" }}>
@@ -497,11 +482,9 @@ const LayerUI = ({
renderCustomFooter={renderCustomFooter}
onImageAction={onImageAction}
renderTopRightUI={renderTopRightUI}
renderMenuLinks={renderMenuLinks}
renderCustomStats={renderCustomStats}
renderSidebars={renderSidebars}
device={device}
UIOptions={UIOptions}
/>
)}

View File

@@ -1,5 +1,5 @@
import React from "react";
import { AppProps, AppState, Device, ExcalidrawProps } from "../types";
import { AppState, Device, ExcalidrawProps } from "../types";
import { ActionManager } from "../actions/manager";
import { t } from "../i18n";
import Stack from "./Stack";
@@ -45,12 +45,10 @@ type MobileMenuProps = {
isMobile: boolean,
appState: AppState,
) => JSX.Element | null;
renderMenuLinks?: ExcalidrawProps["renderMenuLinks"];
renderCustomStats?: ExcalidrawProps["renderCustomStats"];
renderSidebars: () => JSX.Element | null;
device: Device;
renderWelcomeScreen?: boolean;
UIOptions: AppProps["UIOptions"];
};
export const MobileMenu = ({
@@ -68,12 +66,10 @@ export const MobileMenu = ({
renderCustomFooter,
onImageAction,
renderTopRightUI,
renderMenuLinks,
renderCustomStats,
renderSidebars,
device,
renderWelcomeScreen,
UIOptions,
}: MobileMenuProps) => {
const renderToolbar = () => {
return (
@@ -115,8 +111,8 @@ export const MobileMenu = ({
/>
</Stack.Row>
</Island>
{renderTopRightUI && renderTopRightUI(true, appState)}
<div className="mobile-misc-tools-container">
{renderTopRightUI && renderTopRightUI(true, appState)}
<PenModeButton
checked={appState.penMode}
onChange={onPenModeToggle}
@@ -196,14 +192,12 @@ export const MobileMenu = ({
{!appState.viewModeEnabled && actionManager.renderAction("loadScene")}
{renderJSONExportDialog()}
{renderImageExportDialog()}
{UIOptions.canvasActions.saveAsImage && (
<MenuItem
label={t("buttons.exportImage")}
icon={ExportImageIcon}
dataTestId="image-export-button"
onClick={() => setAppState({ openDialog: "imageExport" })}
/>
)}
<MenuItem
label={t("buttons.exportImage")}
icon={ExportImageIcon}
dataTestId="image-export-button"
onClick={() => setAppState({ openDialog: "imageExport" })}
/>
{onCollabButtonClick && (
<CollabButton
isCollaborating={isCollaborating}
@@ -213,16 +207,8 @@ export const MobileMenu = ({
)}
{actionManager.renderAction("toggleShortcuts", undefined, true)}
{!appState.viewModeEnabled && actionManager.renderAction("clearCanvas")}
{typeof renderMenuLinks === "undefined" ? ( //zsviczian
<Separator />
) : (
renderMenuLinks && <Separator />
)}
{typeof renderMenuLinks === "undefined" ? ( //zsviczian
<MenuLinks />
) : (
renderMenuLinks && renderMenuLinks(device.isMobile, appState)
)}
<Separator />
<MenuLinks />
<Separator />
{!appState.viewModeEnabled && (
<div style={{ marginBottom: ".5rem" }}>

View File

@@ -90,10 +90,10 @@ describe("Sidebar", () => {
const sidebar = container.querySelector<HTMLElement>(".test-sidebar");
expect(sidebar).not.toBe(null);
const closeButton = queryByTestId(sidebar!, "sidebar-close")!;
const closeButton = queryByTestId(sidebar!, "sidebar-close");
expect(closeButton).not.toBe(null);
fireEvent.click(closeButton);
fireEvent.click(closeButton!.querySelector("button")!);
await waitFor(() => {
expect(container.querySelector<HTMLElement>(".test-sidebar")).toBe(null);
expect(onClose).toHaveBeenCalled();

View File

@@ -492,7 +492,7 @@ export const getElementBounds = (
export const getCommonBounds = (
elements: readonly ExcalidrawElement[],
): [number, number, number, number] => {
): [minX: number, minY: number, maxX: number, maxY: number] => {
if (!elements.length) {
return [0, 0, 0, 0];
}

View File

@@ -392,8 +392,6 @@ No, Excalidraw package doesn't come with collaboration built in, since the imple
| [`onPointerUpdate`](#onPointerUpdate) | Function | | Callback triggered when mouse pointer is updated. |
| [`langCode`](#langCode) | string | `en` | Language code string |
| [`renderTopRightUI`](#renderTopRightUI) | Function | | Function that renders custom UI in top right corner |
| [`hideWelcomeScreen`](#hideWelcomeScreen) | boolean | | This implies if the app should always hide the welcome sreen |
| [`renderMenuLinks`](#renderMenuLinks) | Function | | Function that renders custom list of links (or other custom UI) in the app menu |
| [`renderFooter `](#renderFooter) | Function | | Function that renders custom UI footer |
| [`renderCustomStats`](#renderCustomStats) | Function | | Function that can be used to render custom stats on the stats dialog. |
| [`renderSIdebar`](#renderSIdebar) | Function | | Render function that renders custom sidebar. |
@@ -615,22 +613,6 @@ import { defaultLang, languages } from "@excalidraw/excalidraw";
A function returning JSX to render custom UI in the top right corner of the app.
#### `hideWelcomeScreen`
<pre>
boolean
</pre>
Boolean value to override the displaying of the welcome screen elements. If set to true, the welcome screen will never be shown.
#### `renderMenuLinks`
<pre>
((isMobile: boolean, appState: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L79">AppState</a>) => JSX | null)|null
</pre>
A function returning JSX to render custom UI (intended to be a list of custom links) replacing the default list of links in the app menu. If set to null, the list of links will not be displayed. If unset, the default list of links will be displayed.
#### `renderFooter`
<pre>
@@ -703,7 +685,7 @@ This prop sets the name of the drawing which will be used when exporting the dra
#### `UIOptions`
This prop can be used to customise UI of Excalidraw. Currently we support customising [`canvasActions`](#canvasActions), [`dockedSidebarBreakpoint`](dockedSidebarBreakpoint), and ['showLanguageList`](showLanguageList). It accepts the below parameters
This prop can be used to customise UI of Excalidraw. Currently we support customising [`canvasActions`](#canvasActions) and [`dockedSidebarBreakpoint`](dockedSidebarBreakpoint). It accepts the below parameters
<pre>
{ canvasActions: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L208"> CanvasActions<a/> }
@@ -727,10 +709,6 @@ This prop indicates at what point should we break to a docked, permanent sidebar
![image](https://user-images.githubusercontent.com/11256141/174664866-c698c3fa-197b-43ff-956c-d79852c7b326.png)
### `showLanguageList`
Boolean prop. If set to `true` the `language selector dropdown list` will be hidden in the app menu. If `false` or `undefined` the language dropdown will be rendered.
#### `exportOpts`
The below attributes can be set in `UIOptions.canvasActions.export` to customize the export dialog. If `UIOptions.canvasActions.export` is `false` the export button will not be rendered.

View File

@@ -20,8 +20,6 @@ const ExcalidrawBase = (props: ExcalidrawProps) => {
isCollaborating = false,
onPointerUpdate,
renderTopRightUI,
hideWelcomeScreen,
renderMenuLinks,
renderFooter,
renderSidebar,
langCode = defaultLang.code,
@@ -95,8 +93,6 @@ const ExcalidrawBase = (props: ExcalidrawProps) => {
isCollaborating={isCollaborating}
onPointerUpdate={onPointerUpdate}
renderTopRightUI={renderTopRightUI}
hideWelcomeScreen={hideWelcomeScreen}
renderMenuLinks={renderMenuLinks}
renderFooter={renderFooter}
langCode={langCode}
viewModeEnabled={viewModeEnabled}

View File

@@ -14,6 +14,102 @@ import {
export const SVG_EXPORT_TAG = `<!-- svg-source:excalidraw -->`;
const getExactBoundingBox = async (
elements: readonly NonDeletedExcalidrawElement[],
appState: {
exportBackground: boolean;
exportPadding?: number;
exportScale?: number;
viewBackgroundColor: string;
exportWithDarkMode?: boolean;
exportEmbedScene?: boolean;
},
files: BinaryFiles,
): Promise<
[offsetLeft: number, offsetTop: number, width: number, height: number]
> => {
const padding = DEFAULT_EXPORT_PADDING;
// const padding = 0;
const [minX, minY, width, height] = getApproximateCanvasSize(
elements,
padding,
);
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
const { imageCache } = await updateImageCache({
imageCache: new Map(),
fileIds: getInitializedImageElements(elements).map(
(element) => element.fileId,
),
files,
});
const defaultAppState = getDefaultAppState();
renderScene({
elements,
// @ts-ignore
appState,
scale: 1,
rc: rough.canvas(canvas),
canvas,
renderConfig: {
viewBackgroundColor: null,
scrollX: -minX + padding,
scrollY: -minY + padding,
zoom: defaultAppState.zoom,
remotePointerViewportCoords: {},
remoteSelectedElementIds: {},
shouldCacheIgnoreZoom: false,
remotePointerUsernames: {},
remotePointerUserStates: {},
theme: "light",
imageCache,
renderScrollbars: false,
renderSelection: false,
renderGrid: false,
isExporting: true,
},
});
const ctx = canvas.getContext("2d")!;
const { data } = ctx.getImageData(0, 0, width, height);
let _minX = Infinity;
let _minY = Infinity;
let _maxX = -Infinity;
let _maxY = -Infinity;
const rows = [];
let row: number[][] = [];
for (let i = 0; i < data.length - 1; i = i + 4) {
if (i && i % (width * 4) === 0) {
rows.push(row);
row = [];
}
const pixel = [data[i], data[i + 1], data[i + 2], data[i + 3]];
row.push(pixel);
}
for (const [y, row] of rows.entries()) {
for (const [x, pixel] of row.entries()) {
if (pixel[3] > 0) {
_minX = Math.min(_minX, x);
_minY = Math.min(_minY, y);
_maxX = Math.max(_maxX, x);
_maxY = Math.max(_maxY, y);
}
}
}
const offsetLeft = padding - _minX;
const offsetTop = padding - _minY;
return [offsetLeft, offsetTop, _maxX - _minX, _maxY - _minY];
};
export const exportToCanvas = async (
elements: readonly NonDeletedExcalidrawElement[],
appState: AppState,
@@ -37,7 +133,12 @@ export const exportToCanvas = async (
return { canvas, scale: appState.exportScale };
},
) => {
const [minX, minY, width, height] = getCanvasSize(elements, exportPadding);
const [scrollX, scrollY, width, height] = await getCanvasSize(
elements,
appState,
files,
exportPadding,
);
const { canvas, scale = 1 } = createCanvas(width, height);
@@ -59,8 +160,8 @@ export const exportToCanvas = async (
canvas,
renderConfig: {
viewBackgroundColor: exportBackground ? viewBackgroundColor : null,
scrollX: -minX + exportPadding,
scrollY: -minY + exportPadding,
scrollX,
scrollY,
zoom: defaultAppState.zoom,
remotePointerViewportCoords: {},
remoteSelectedElementIds: {},
@@ -109,7 +210,12 @@ export const exportToSvg = async (
console.error(error);
}
}
const [minX, minY, width, height] = getCanvasSize(elements, exportPadding);
const [minX, minY, width, height] = await getCanvasSize(
elements,
appState,
files || {},
exportPadding,
);
// initialize SVG root
const svgRoot = document.createElementNS(SVG_NS, "svg");
@@ -172,26 +278,66 @@ export const exportToSvg = async (
return svgRoot;
};
// calculate smallest area to fit the contents in
const getCanvasSize = (
const getApproximateCanvasSize = (
elements: readonly NonDeletedExcalidrawElement[],
exportPadding: number,
): [number, number, number, number] => {
const [minX, minY, maxX, maxY] = getCommonBounds(elements);
const bounds = getCommonBounds(elements);
const minX = Math.floor(bounds[0]);
const minY = Math.floor(bounds[1]);
const maxX = Math.ceil(bounds[2]);
const maxY = Math.ceil(bounds[3]);
const width = distance(minX, maxX) + exportPadding * 2;
const height = distance(minY, maxY) + exportPadding + exportPadding;
const height =
Math.ceil(distance(minY, maxY)) + exportPadding + exportPadding;
return [minX, minY, width, height];
};
// calculate smallest area to fit the contents in
const getCanvasSize = async (
elements: readonly NonDeletedExcalidrawElement[],
appState: {
exportBackground: boolean;
exportPadding?: number;
exportScale?: number;
viewBackgroundColor: string;
exportWithDarkMode?: boolean;
exportEmbedScene?: boolean;
},
files: BinaryFiles,
exportPadding: number,
): Promise<[number, number, number, number]> => {
if (exportPadding) {
const [minX, minY, width, height] = getApproximateCanvasSize(
elements,
exportPadding,
);
return [-minX + exportPadding, -minY + exportPadding, width, height];
} else {
const [minX, minY] = getApproximateCanvasSize(elements, exportPadding);
const [offsetLeft, offsetRight, width, height] = await getExactBoundingBox(
elements,
appState,
files,
);
return [-minX + offsetLeft, -minY + offsetRight, width, height];
}
};
export const getExportSize = (
elements: readonly NonDeletedExcalidrawElement[],
exportPadding: number,
scale: number,
): [number, number] => {
const [, , width, height] = getCanvasSize(elements, exportPadding).map(
(dimension) => Math.trunc(dimension * scale),
);
const [, , width, height] = getApproximateCanvasSize(
elements,
exportPadding,
).map((dimension) => Math.trunc(dimension * scale));
return [width, height];
};

View File

@@ -3,8 +3,6 @@ import "jest-canvas-mock";
import dotenv from "dotenv";
import polyfill from "./polyfill";
require("fake-indexeddb/auto");
polyfill();
// jest doesn't know of .env.development so we need to init it ourselves
dotenv.config({

View File

@@ -1951,9 +1951,7 @@ Object {
"penDetected": false,
"penMode": false,
"pendingImageElementId": null,
"previousSelectedElementIds": Object {
"id0": true,
},
"previousSelectedElementIds": Object {},
"resizingElement": null,
"scrollX": 0,
"scrollY": 0,
@@ -1990,7 +1988,7 @@ Object {
"boundElements": null,
"fillStyle": "hachure",
"groupIds": Array [],
"height": 10,
"height": 15,
"id": "id0",
"isDeleted": false,
"link": null,
@@ -2006,9 +2004,9 @@ Object {
"updated": 1,
"version": 3,
"versionNonce": 453191,
"width": 10,
"x": 25,
"y": 25,
"width": 15,
"x": 10,
"y": 10,
}
`;
@@ -2087,7 +2085,7 @@ Object {
"boundElements": null,
"fillStyle": "hachure",
"groupIds": Array [],
"height": 10,
"height": 15,
"id": "id0",
"isDeleted": false,
"link": null,
@@ -2103,9 +2101,9 @@ Object {
"updated": 1,
"version": 3,
"versionNonce": 453191,
"width": 10,
"x": 25,
"y": 25,
"width": 15,
"x": 10,
"y": 10,
},
],
},
@@ -2684,9 +2682,7 @@ Object {
"penDetected": false,
"penMode": false,
"pendingImageElementId": null,
"previousSelectedElementIds": Object {
"id0": true,
},
"previousSelectedElementIds": Object {},
"resizingElement": null,
"scrollX": 0,
"scrollY": 0,
@@ -2717,35 +2713,6 @@ Object {
`;
exports[`regression tests alt-drag duplicates an element: [end of test] element 0 1`] = `
Object {
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
"fillStyle": "hachure",
"groupIds": Array [],
"height": 10,
"id": "id0_copy",
"isDeleted": false,
"link": null,
"locked": false,
"opacity": 100,
"roughness": 1,
"seed": 401146281,
"strokeColor": "#000000",
"strokeSharpness": "sharp",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
"updated": 1,
"version": 4,
"versionNonce": 2019559783,
"width": 10,
"x": 10,
"y": 10,
}
`;
exports[`regression tests alt-drag duplicates an element: [end of test] element 1 1`] = `
Object {
"angle": 0,
"backgroundColor": "transparent",
@@ -2766,11 +2733,11 @@ Object {
"strokeWidth": 1,
"type": "rectangle",
"updated": 1,
"version": 3,
"versionNonce": 453191,
"version": 2,
"versionNonce": 1278240551,
"width": 10,
"x": 20,
"y": 20,
"x": 10,
"y": 10,
}
`;
@@ -2830,78 +2797,11 @@ Object {
},
],
},
Object {
"appState": Object {
"editingGroupId": null,
"editingLinearElement": null,
"name": "Untitled-201933152653",
"selectedElementIds": Object {
"id0": true,
"id1": true,
},
"selectedGroupIds": Object {},
"viewBackgroundColor": "#ffffff",
},
"elements": Array [
Object {
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
"fillStyle": "hachure",
"groupIds": Array [],
"height": 10,
"id": "id0_copy",
"isDeleted": false,
"link": null,
"locked": false,
"opacity": 100,
"roughness": 1,
"seed": 401146281,
"strokeColor": "#000000",
"strokeSharpness": "sharp",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
"updated": 1,
"version": 4,
"versionNonce": 2019559783,
"width": 10,
"x": 10,
"y": 10,
},
Object {
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
"fillStyle": "hachure",
"groupIds": Array [],
"height": 10,
"id": "id0",
"isDeleted": false,
"link": null,
"locked": false,
"opacity": 100,
"roughness": 1,
"seed": 337897,
"strokeColor": "#000000",
"strokeSharpness": "sharp",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
"updated": 1,
"version": 3,
"versionNonce": 453191,
"width": 10,
"x": 20,
"y": 20,
},
],
},
],
}
`;
exports[`regression tests alt-drag duplicates an element: [end of test] number of elements 1`] = `2`;
exports[`regression tests alt-drag duplicates an element: [end of test] number of elements 1`] = `1`;
exports[`regression tests alt-drag duplicates an element: [end of test] number of renders 1`] = `12`;
@@ -3837,230 +3737,6 @@ exports[`regression tests change the properties of a shape: [end of test] number
exports[`regression tests change the properties of a shape: [end of test] number of renders 1`] = `15`;
exports[`regression tests click on an element and drag it: [dragged] appState 1`] = `
Object {
"activeTool": Object {
"customType": null,
"lastActiveToolBeforeEraser": null,
"locked": false,
"type": "selection",
},
"collaborators": Map {},
"currentChartType": "bar",
"currentItemBackgroundColor": "transparent",
"currentItemEndArrowhead": "arrow",
"currentItemFillStyle": "hachure",
"currentItemFontFamily": 1,
"currentItemFontSize": 20,
"currentItemLinearStrokeSharpness": "round",
"currentItemOpacity": 100,
"currentItemRoughness": 1,
"currentItemStartArrowhead": null,
"currentItemStrokeColor": "#000000",
"currentItemStrokeSharpness": "sharp",
"currentItemStrokeStyle": "solid",
"currentItemStrokeWidth": 1,
"currentItemTextAlign": "left",
"cursorButton": "up",
"draggingElement": null,
"editingElement": null,
"editingGroupId": null,
"editingLinearElement": null,
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLoading": false,
"isResizing": false,
"isRotating": false,
"isSidebarDocked": false,
"lastPointerDownWith": "mouse",
"multiElement": null,
"name": "Untitled-201933152653",
"offsetLeft": 0,
"offsetTop": 0,
"openDialog": null,
"openMenu": null,
"openPopup": null,
"openSidebar": null,
"pasteDialog": Object {
"data": null,
"shown": false,
},
"penDetected": false,
"penMode": false,
"pendingImageElementId": null,
"previousSelectedElementIds": Object {
"id0": true,
},
"resizingElement": null,
"scrollX": 0,
"scrollY": 0,
"scrolledOutside": false,
"selectedElementIds": Object {
"id0": true,
"id1": true,
},
"selectedGroupIds": Object {},
"selectedLinearElement": null,
"selectionElement": null,
"shouldCacheIgnoreZoom": false,
"showHyperlinkPopup": false,
"showStats": false,
"showWelcomeScreen": true,
"startBoundElement": null,
"suggestedBindings": Array [],
"theme": "light",
"toast": null,
"viewBackgroundColor": "#ffffff",
"viewModeEnabled": false,
"width": 1024,
"zenModeEnabled": false,
"zoom": Object {
"value": 1,
},
}
`;
exports[`regression tests click on an element and drag it: [dragged] element 0 1`] = `
Object {
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
"fillStyle": "hachure",
"groupIds": Array [],
"height": 10,
"id": "id0",
"isDeleted": false,
"link": null,
"locked": false,
"opacity": 100,
"roughness": 1,
"seed": 337897,
"strokeColor": "#000000",
"strokeSharpness": "sharp",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
"updated": 1,
"version": 3,
"versionNonce": 453191,
"width": 10,
"x": 20,
"y": 20,
}
`;
exports[`regression tests click on an element and drag it: [dragged] history 1`] = `
Object {
"recording": false,
"redoStack": Array [],
"stateHistory": Array [
Object {
"appState": Object {
"editingGroupId": null,
"editingLinearElement": null,
"name": "Untitled-201933152653",
"selectedElementIds": Object {},
"selectedGroupIds": Object {},
"viewBackgroundColor": "#ffffff",
},
"elements": Array [],
},
Object {
"appState": Object {
"editingGroupId": null,
"editingLinearElement": null,
"name": "Untitled-201933152653",
"selectedElementIds": Object {
"id0": true,
},
"selectedGroupIds": Object {},
"viewBackgroundColor": "#ffffff",
},
"elements": Array [
Object {
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
"fillStyle": "hachure",
"groupIds": Array [],
"height": 10,
"id": "id0",
"isDeleted": false,
"link": null,
"locked": false,
"opacity": 100,
"roughness": 1,
"seed": 337897,
"strokeColor": "#000000",
"strokeSharpness": "sharp",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
"updated": 1,
"version": 2,
"versionNonce": 1278240551,
"width": 10,
"x": 10,
"y": 10,
},
],
},
Object {
"appState": Object {
"editingGroupId": null,
"editingLinearElement": null,
"name": "Untitled-201933152653",
"selectedElementIds": Object {
"id0": true,
"id1": true,
},
"selectedGroupIds": Object {},
"viewBackgroundColor": "#ffffff",
},
"elements": Array [
Object {
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
"fillStyle": "hachure",
"groupIds": Array [],
"height": 10,
"id": "id0",
"isDeleted": false,
"link": null,
"locked": false,
"opacity": 100,
"roughness": 1,
"seed": 337897,
"strokeColor": "#000000",
"strokeSharpness": "sharp",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
"updated": 1,
"version": 3,
"versionNonce": 453191,
"width": 10,
"x": 20,
"y": 20,
},
],
},
],
}
`;
exports[`regression tests click on an element and drag it: [dragged] number of elements 1`] = `1`;
exports[`regression tests click on an element and drag it: [dragged] number of renders 1`] = `12`;
exports[`regression tests click on an element and drag it: [end of test] appState 1`] = `
Object {
"activeTool": Object {
@@ -4119,10 +3795,7 @@ Object {
"penDetected": false,
"penMode": false,
"pendingImageElementId": null,
"previousSelectedElementIds": Object {
"id0": true,
"id1": true,
},
"previousSelectedElementIds": Object {},
"resizingElement": null,
"scrollX": 0,
"scrollY": 0,
@@ -4130,7 +3803,6 @@ Object {
"selectedElementIds": Object {
"id0": true,
"id1": true,
"id2": true,
},
"selectedGroupIds": Object {},
"selectedLinearElement": null,
@@ -4174,8 +3846,8 @@ Object {
"strokeWidth": 1,
"type": "rectangle",
"updated": 1,
"version": 4,
"versionNonce": 2019559783,
"version": 2,
"versionNonce": 1278240551,
"width": 10,
"x": 10,
"y": 10,
@@ -4238,96 +3910,13 @@ Object {
},
],
},
Object {
"appState": Object {
"editingGroupId": null,
"editingLinearElement": null,
"name": "Untitled-201933152653",
"selectedElementIds": Object {
"id0": true,
"id1": true,
},
"selectedGroupIds": Object {},
"viewBackgroundColor": "#ffffff",
},
"elements": Array [
Object {
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
"fillStyle": "hachure",
"groupIds": Array [],
"height": 10,
"id": "id0",
"isDeleted": false,
"link": null,
"locked": false,
"opacity": 100,
"roughness": 1,
"seed": 337897,
"strokeColor": "#000000",
"strokeSharpness": "sharp",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
"updated": 1,
"version": 3,
"versionNonce": 453191,
"width": 10,
"x": 20,
"y": 20,
},
],
},
Object {
"appState": Object {
"editingGroupId": null,
"editingLinearElement": null,
"name": "Untitled-201933152653",
"selectedElementIds": Object {
"id0": true,
"id1": true,
"id2": true,
},
"selectedGroupIds": Object {},
"viewBackgroundColor": "#ffffff",
},
"elements": Array [
Object {
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
"fillStyle": "hachure",
"groupIds": Array [],
"height": 10,
"id": "id0",
"isDeleted": false,
"link": null,
"locked": false,
"opacity": 100,
"roughness": 1,
"seed": 337897,
"strokeColor": "#000000",
"strokeSharpness": "sharp",
"strokeStyle": "solid",
"strokeWidth": 1,
"type": "rectangle",
"updated": 1,
"version": 4,
"versionNonce": 2019559783,
"width": 10,
"x": 10,
"y": 10,
},
],
},
],
}
`;
exports[`regression tests click on an element and drag it: [end of test] number of elements 1`] = `1`;
exports[`regression tests click on an element and drag it: [end of test] number of renders 1`] = `15`;
exports[`regression tests click on an element and drag it: [end of test] number of renders 1`] = `12`;
exports[`regression tests click to select a shape: [end of test] appState 1`] = `
Object {
@@ -5924,14 +5513,13 @@ Object {
"penDetected": false,
"penMode": false,
"pendingImageElementId": null,
"previousSelectedElementIds": Object {
"id0": true,
},
"previousSelectedElementIds": Object {},
"resizingElement": null,
"scrollX": 0,
"scrollY": 0,
"scrolledOutside": false,
"selectedElementIds": Object {
"id0": true,
"id1": true,
},
"selectedGroupIds": Object {},
@@ -14112,7 +13700,7 @@ Object {
exports[`regression tests rerenders UI on language change: [end of test] number of elements 1`] = `0`;
exports[`regression tests rerenders UI on language change: [end of test] number of renders 1`] = `10`;
exports[`regression tests rerenders UI on language change: [end of test] number of renders 1`] = `6`;
exports[`regression tests shift click on selected element should deselect it on pointer up: [end of test] appState 1`] = `
Object {
@@ -14172,15 +13760,13 @@ Object {
"penDetected": false,
"penMode": false,
"pendingImageElementId": null,
"previousSelectedElementIds": Object {
"id0": true,
},
"previousSelectedElementIds": Object {},
"resizingElement": null,
"scrollX": 0,
"scrollY": 0,
"scrolledOutside": false,
"selectedElementIds": Object {
"id0": false,
"id0": true,
"id1": true,
},
"selectedGroupIds": Object {},

View File

@@ -42,7 +42,7 @@ describe("Test dragCreate", () => {
// finish (position does not matter)
fireEvent.pointerUp(canvas);
expect(renderScene).toHaveBeenCalledTimes(9);
expect(renderScene).toHaveBeenCalledTimes(8);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(1);
@@ -73,7 +73,7 @@ describe("Test dragCreate", () => {
// finish (position does not matter)
fireEvent.pointerUp(canvas);
expect(renderScene).toHaveBeenCalledTimes(9);
expect(renderScene).toHaveBeenCalledTimes(8);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(1);
@@ -104,7 +104,7 @@ describe("Test dragCreate", () => {
// finish (position does not matter)
fireEvent.pointerUp(canvas);
expect(renderScene).toHaveBeenCalledTimes(9);
expect(renderScene).toHaveBeenCalledTimes(8);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(1);
@@ -135,7 +135,7 @@ describe("Test dragCreate", () => {
// finish (position does not matter)
fireEvent.pointerUp(canvas);
expect(renderScene).toHaveBeenCalledTimes(9);
expect(renderScene).toHaveBeenCalledTimes(8);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(1);
@@ -170,7 +170,7 @@ describe("Test dragCreate", () => {
// finish (position does not matter)
fireEvent.pointerUp(canvas);
expect(renderScene).toHaveBeenCalledTimes(9);
expect(renderScene).toHaveBeenCalledTimes(8);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(1);
@@ -210,7 +210,7 @@ describe("Test dragCreate", () => {
// finish (position does not matter)
fireEvent.pointerUp(canvas);
expect(renderScene).toHaveBeenCalledTimes(7);
expect(renderScene).toHaveBeenCalledTimes(6);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(0);
});
@@ -229,7 +229,7 @@ describe("Test dragCreate", () => {
// finish (position does not matter)
fireEvent.pointerUp(canvas);
expect(renderScene).toHaveBeenCalledTimes(7);
expect(renderScene).toHaveBeenCalledTimes(6);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(0);
});
@@ -248,7 +248,7 @@ describe("Test dragCreate", () => {
// finish (position does not matter)
fireEvent.pointerUp(canvas);
expect(renderScene).toHaveBeenCalledTimes(7);
expect(renderScene).toHaveBeenCalledTimes(6);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(0);
});
@@ -272,7 +272,7 @@ describe("Test dragCreate", () => {
key: KEYS.ENTER,
});
expect(renderScene).toHaveBeenCalledTimes(8);
expect(renderScene).toHaveBeenCalledTimes(7);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(0);
});
@@ -296,7 +296,7 @@ describe("Test dragCreate", () => {
key: KEYS.ENTER,
});
expect(renderScene).toHaveBeenCalledTimes(8);
expect(renderScene).toHaveBeenCalledTimes(7);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(0);
});

View File

@@ -1,6 +1,4 @@
import { fireEvent, render, waitFor } from "./test-utils";
import { queryByTestId } from "@testing-library/react";
import ExcalidrawApp from "../excalidraw-app";
import { API } from "./helpers/api";
import { MIME_TYPES } from "../constants";
@@ -95,11 +93,15 @@ describe("library menu", () => {
const latestLibrary = await h.app.library.getLatestLibrary();
expect(latestLibrary.length).toBe(0);
const libraryButton = container.querySelector(".library-button");
const libraryButton = container.querySelector(".ToolIcon__library");
fireEvent.click(libraryButton!);
fireEvent.click(container.querySelector(".Sidebar__dropdown-btn")!);
queryByTestId(container, "lib-dropdown--load")!.click();
const loadLibraryButton = container.querySelector(
".library-actions .library-actions--load",
);
fireEvent.click(loadLibraryButton!);
const libraryItems = parseLibraryJSON(await libraryJSONPromise);

View File

@@ -38,7 +38,7 @@ describe("move element", () => {
fireEvent.pointerMove(canvas, { clientX: 60, clientY: 70 });
fireEvent.pointerUp(canvas);
expect(renderScene).toHaveBeenCalledTimes(9);
expect(renderScene).toHaveBeenCalledTimes(8);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(1);
expect(h.state.selectedElementIds[h.elements[0].id]).toBeTruthy();
@@ -77,7 +77,7 @@ describe("move element", () => {
// select the second rectangles
new Pointer("mouse").clickOn(rectB);
expect(renderScene).toHaveBeenCalledTimes(23);
expect(renderScene).toHaveBeenCalledTimes(22);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(3);
expect(h.state.selectedElementIds[rectB.id]).toBeTruthy();
@@ -120,7 +120,7 @@ describe("duplicate element on move when ALT is clicked", () => {
fireEvent.pointerMove(canvas, { clientX: 60, clientY: 70 });
fireEvent.pointerUp(canvas);
expect(renderScene).toHaveBeenCalledTimes(9);
expect(renderScene).toHaveBeenCalledTimes(8);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(1);
expect(h.state.selectedElementIds[h.elements[0].id]).toBeTruthy();

View File

@@ -42,7 +42,7 @@ describe("remove shape in non linear elements", () => {
fireEvent.pointerDown(canvas, { clientX: 30, clientY: 20 });
fireEvent.pointerUp(canvas, { clientX: 30, clientY: 30 });
expect(renderScene).toHaveBeenCalledTimes(7);
expect(renderScene).toHaveBeenCalledTimes(6);
expect(h.elements.length).toEqual(0);
});
@@ -56,7 +56,7 @@ describe("remove shape in non linear elements", () => {
fireEvent.pointerDown(canvas, { clientX: 30, clientY: 20 });
fireEvent.pointerUp(canvas, { clientX: 30, clientY: 30 });
expect(renderScene).toHaveBeenCalledTimes(7);
expect(renderScene).toHaveBeenCalledTimes(6);
expect(h.elements.length).toEqual(0);
});
@@ -70,7 +70,7 @@ describe("remove shape in non linear elements", () => {
fireEvent.pointerDown(canvas, { clientX: 30, clientY: 20 });
fireEvent.pointerUp(canvas, { clientX: 30, clientY: 30 });
expect(renderScene).toHaveBeenCalledTimes(7);
expect(renderScene).toHaveBeenCalledTimes(6);
expect(h.elements.length).toEqual(0);
});
});
@@ -102,7 +102,7 @@ describe("multi point mode in linear elements", () => {
key: KEYS.ENTER,
});
expect(renderScene).toHaveBeenCalledTimes(15);
expect(renderScene).toHaveBeenCalledTimes(14);
expect(h.elements.length).toEqual(1);
const element = h.elements[0] as ExcalidrawLinearElement;
@@ -145,7 +145,7 @@ describe("multi point mode in linear elements", () => {
key: KEYS.ENTER,
});
expect(renderScene).toHaveBeenCalledTimes(15);
expect(renderScene).toHaveBeenCalledTimes(14);
expect(h.elements.length).toEqual(1);
const element = h.elements[0] as ExcalidrawLinearElement;

File diff suppressed because it is too large Load Diff

View File

@@ -90,10 +90,7 @@ describe("<Excalidraw/>", () => {
describe("Test theme prop", () => {
it("should show the theme toggle by default", async () => {
const { container } = await render(<Excalidraw />);
expect(h.state.theme).toBe(THEME.LIGHT);
queryByTestId(container, "menu-button")!.click();
const darkModeToggle = queryByTestId(container, "toggle-dark-mode");
expect(darkModeToggle).toBeTruthy();
});

View File

@@ -10,7 +10,6 @@ const toolMap = {
line: "line",
freedraw: "freedraw",
text: "text",
eraser: "eraser",
};
export type ToolName = keyof typeof toolMap;

View File

@@ -174,7 +174,7 @@ describe("regression tests", () => {
mouse.up(10, 10);
const { x: prevX, y: prevY } = API.getSelectedElement();
mouse.down(-8, -8);
mouse.down(-10, -10);
mouse.up(10, 10);
const { x: nextX, y: nextY } = API.getSelectedElement();
@@ -201,7 +201,7 @@ describe("regression tests", () => {
).toBe(1);
Keyboard.withModifierKeys({ alt: true }, () => {
mouse.down(-8, -8);
mouse.down(-10, -10);
mouse.up(10, 10);
});
@@ -446,8 +446,6 @@ describe("regression tests", () => {
UI.clickTool("rectangle");
// english lang should display `thin` label
expect(screen.queryByTitle(/thin/i)).not.toBeNull();
fireEvent.click(document.querySelector(".menu-button")!);
fireEvent.change(document.querySelector(".dropdown-select__language")!, {
target: { value: "de-DE" },
});
@@ -674,10 +672,9 @@ describe("regression tests", () => {
mouse.down();
mouse.up(100, 100);
expect(API.getSelectedElements().length).toBe(1);
// hits bounding box without hitting element
mouse.down(98, 98);
mouse.down();
expect(API.getSelectedElements().length).toBe(1);
mouse.up();
expect(API.getSelectedElements().length).toBe(0);
});
@@ -747,7 +744,7 @@ describe("regression tests", () => {
// drag element from point on bounding box that doesn't hit element
mouse.reset();
mouse.down(8, 8);
mouse.down();
mouse.up(25, 25);
expect(API.getSelectedElement().x).toEqual(prevX + 25);
@@ -1023,7 +1020,7 @@ describe("regression tests", () => {
// Rectangle is already selected since creating
// it was our last action
Keyboard.withModifierKeys({ shift: true }, () => {
mouse.down(-8, -8);
mouse.down();
});
expect(API.getSelectedElements().length).toBe(1);

View File

@@ -154,7 +154,7 @@ describe("selection element", () => {
const canvas = container.querySelector("canvas")!;
fireEvent.pointerDown(canvas, { clientX: 60, clientY: 100 });
expect(renderScene).toHaveBeenCalledTimes(5);
expect(renderScene).toHaveBeenCalledTimes(4);
const selectionElement = h.state.selectionElement!;
expect(selectionElement).not.toBeNull();
expect(selectionElement.type).toEqual("selection");
@@ -175,7 +175,7 @@ describe("selection element", () => {
fireEvent.pointerDown(canvas, { clientX: 60, clientY: 100 });
fireEvent.pointerMove(canvas, { clientX: 150, clientY: 30 });
expect(renderScene).toHaveBeenCalledTimes(6);
expect(renderScene).toHaveBeenCalledTimes(5);
const selectionElement = h.state.selectionElement!;
expect(selectionElement).not.toBeNull();
expect(selectionElement.type).toEqual("selection");
@@ -197,7 +197,7 @@ describe("selection element", () => {
fireEvent.pointerMove(canvas, { clientX: 150, clientY: 30 });
fireEvent.pointerUp(canvas);
expect(renderScene).toHaveBeenCalledTimes(7);
expect(renderScene).toHaveBeenCalledTimes(6);
expect(h.state.selectionElement).toBeNull();
});
});
@@ -232,7 +232,7 @@ describe("select single element on the scene", () => {
fireEvent.pointerDown(canvas, { clientX: 45, clientY: 20 });
fireEvent.pointerUp(canvas);
expect(renderScene).toHaveBeenCalledTimes(11);
expect(renderScene).toHaveBeenCalledTimes(10);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(1);
expect(h.state.selectedElementIds[h.elements[0].id]).toBeTruthy();
@@ -261,7 +261,7 @@ describe("select single element on the scene", () => {
fireEvent.pointerDown(canvas, { clientX: 45, clientY: 20 });
fireEvent.pointerUp(canvas);
expect(renderScene).toHaveBeenCalledTimes(11);
expect(renderScene).toHaveBeenCalledTimes(10);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(1);
expect(h.state.selectedElementIds[h.elements[0].id]).toBeTruthy();
@@ -290,7 +290,7 @@ describe("select single element on the scene", () => {
fireEvent.pointerDown(canvas, { clientX: 45, clientY: 20 });
fireEvent.pointerUp(canvas);
expect(renderScene).toHaveBeenCalledTimes(11);
expect(renderScene).toHaveBeenCalledTimes(10);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(1);
expect(h.state.selectedElementIds[h.elements[0].id]).toBeTruthy();
@@ -332,7 +332,7 @@ describe("select single element on the scene", () => {
fireEvent.pointerDown(canvas, { clientX: 40, clientY: 40 });
fireEvent.pointerUp(canvas);
expect(renderScene).toHaveBeenCalledTimes(11);
expect(renderScene).toHaveBeenCalledTimes(10);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(1);
expect(h.state.selectedElementIds[h.elements[0].id]).toBeTruthy();
@@ -373,7 +373,7 @@ describe("select single element on the scene", () => {
fireEvent.pointerDown(canvas, { clientX: 40, clientY: 40 });
fireEvent.pointerUp(canvas);
expect(renderScene).toHaveBeenCalledTimes(11);
expect(renderScene).toHaveBeenCalledTimes(10);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(1);
expect(h.state.selectedElementIds[h.elements[0].id]).toBeTruthy();

View File

@@ -16,6 +16,8 @@ import { SceneData } from "../types";
import { getSelectedElements } from "../scene/selection";
import { ExcalidrawElement } from "../element/types";
require("fake-indexeddb/auto");
const customQueries = {
...queries,
...toolQueries,

View File

@@ -113,6 +113,7 @@ export type AppState = {
exportEmbedScene: boolean;
exportWithDarkMode: boolean;
exportScale: number;
exportPadding: number;
currentItemStrokeColor: string;
currentItemBackgroundColor: string;
currentItemFillStyle: ExcalidrawElement["fillStyle"];
@@ -284,10 +285,6 @@ export interface ExcalidrawProps {
isMobile: boolean,
appState: AppState,
) => JSX.Element | null;
renderMenuLinks?:
| ((isMobile: boolean, appState: AppState) => JSX.Element | null)
| null;
hideWelcomeScreen?: boolean;
renderFooter?: (isMobile: boolean, appState: AppState) => JSX.Element | null;
langCode?: Language["code"];
viewModeEnabled?: boolean;
@@ -303,7 +300,6 @@ export interface ExcalidrawProps {
UIOptions?: {
dockedSidebarBreakpoint?: number;
canvasActions?: CanvasActions;
showLanguageList?: boolean;
};
detectScroll?: boolean;
handleKeyboardGlobally?: boolean;
@@ -376,7 +372,6 @@ export type AppProps = Merge<
UIOptions: {
canvasActions: Required<CanvasActions> & { export: ExportOpts };
dockedSidebarBreakpoint?: number;
showLanguageList?: boolean;
};
detectScroll: boolean;
handleKeyboardGlobally: boolean;