diff --git a/src/components/App.tsx b/src/components/App.tsx index e637e445..1ff1005f 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -31,11 +31,12 @@ import { getDragOffsetXY, dragNewElement, hitTest, + isHittingElementBoundingBoxWithoutHittingElement, } from "../element"; import { getElementsWithinSelection, isOverScrollBars, - getElementAtPosition, + getElementsAtPosition, getElementContainingPosition, getNormalizedZoom, getSelectedElements, @@ -151,9 +152,11 @@ import throttle from "lodash.throttle"; import { LinearElementEditor } from "../element/linearElementEditor"; import { getSelectedGroupIds, + isSelectedViaGroup, selectGroupsForSelectedElements, isElementInGroup, getSelectedGroupIdForElement, + getElementsInGroup, } from "../groups"; import { Library } from "../data/library"; import Scene from "../scene/Scene"; @@ -231,12 +234,16 @@ type PointerDownState = Readonly<{ hit: { // The element the pointer is "hitting", is determined on the initial // pointer down event - element: ExcalidrawElement | null; + element: NonDeleted | null; + // The elements the pointer is "hitting", is determined on the initial + // pointer down event + allHitElements: NonDeleted[]; // This is determined on the initial pointer down event wasAddedToSelection: boolean; // Whether selected element(s) were duplicated, might change during the - // pointer interation + // pointer interaction hasBeenDuplicated: boolean; + hasHitCommonBoundingBoxOfSelectedElements: boolean; }; drag: { // Might change during the pointer interation @@ -1713,7 +1720,32 @@ class App extends React.Component { x: number, y: number, ): NonDeleted | null { - return getElementAtPosition(this.scene.getElements(), (element) => + const allHitElements = this.getElementsAtPosition(x, y); + if (allHitElements.length > 1) { + const elementWithHighestZIndex = + allHitElements[allHitElements.length - 1]; + // If we're hitting element with highest z-index only on its bounding box + // while also hitting other element figure, the latter should be considered. + return isHittingElementBoundingBoxWithoutHittingElement( + elementWithHighestZIndex, + this.state, + x, + y, + ) + ? allHitElements[allHitElements.length - 2] + : elementWithHighestZIndex; + } + if (allHitElements.length === 1) { + return allHitElements[0]; + } + return null; + } + + private getElementsAtPosition( + x: number, + y: number, + ): NonDeleted[] { + return getElementsAtPosition(this.scene.getElements(), (element) => hitTest(element, this.state, x, y), ); } @@ -2084,14 +2116,27 @@ class App extends React.Component { return; } } - const hitElement = this.getElementAtPosition(scenePointerX, scenePointerY); + + const hitElement = this.getElementAtPosition( + scenePointer.x, + scenePointer.y, + ); if (this.state.elementType === "text") { document.documentElement.style.cursor = isTextElement(hitElement) ? CURSOR_TYPE.TEXT : CURSOR_TYPE.CROSSHAIR; + } else if (isOverScrollBar) { + document.documentElement.style.cursor = CURSOR_TYPE.AUTO; + } else if ( + hitElement || + this.isHittingCommonBoundingBoxOfSelectedElements( + scenePointer, + selectedElements, + ) + ) { + document.documentElement.style.cursor = CURSOR_TYPE.MOVE; } else { - document.documentElement.style.cursor = - hitElement && !isOverScrollBar ? "move" : ""; + document.documentElement.style.cursor = CURSOR_TYPE.AUTO; } }; @@ -2370,8 +2415,13 @@ class App extends React.Component { }, hit: { element: null, + allHitElements: [], wasAddedToSelection: false, hasBeenDuplicated: false, + hasHitCommonBoundingBoxOfSelectedElements: this.isHittingCommonBoundingBoxOfSelectedElements( + origin, + selectedElements, + ), }, drag: { hasOccurred: false, @@ -2516,13 +2566,26 @@ class App extends React.Component { pointerDownState.origin.y, ); - this.maybeClearSelectionWhenHittingElement( - event, - pointerDownState.hit.element, + // For overlapped elements one position may hit + // multiple elements + pointerDownState.hit.allHitElements = this.getElementsAtPosition( + pointerDownState.origin.x, + pointerDownState.origin.y, ); - // If we click on something const hitElement = pointerDownState.hit.element; + const someHitElementIsSelected = pointerDownState.hit.allHitElements.some( + (element) => this.isASelectedElement(element), + ); + if ( + (hitElement === null || !someHitElementIsSelected) && + !event.shiftKey && + !pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements + ) { + this.clearSelection(hitElement); + } + + // If we click on something if (hitElement != null) { // deselect if item is selected // if shift is not clicked, this will always return true @@ -2542,23 +2605,28 @@ class App extends React.Component { }); return true; } - this.setState((prevState) => { - return selectGroupsForSelectedElements( - { - ...prevState, - selectedElementIds: { - ...prevState.selectedElementIds, - [hitElement!.id]: true, + + // Add hit element to selection. At this point if we're not holding + // SHIFT the previously selected element(s) were deselected above + // (make sure you use setState updater to use latest state) + if ( + !someHitElementIsSelected && + !pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements + ) { + this.setState((prevState) => { + return selectGroupsForSelectedElements( + { + ...prevState, + selectedElementIds: { + ...prevState.selectedElementIds, + [hitElement!.id]: true, + }, }, - }, - this.scene.getElements(), - ); - }); - // TODO: this is strange... - this.scene.replaceAllElements( - this.scene.getElementsIncludingDeleted(), - ); - pointerDownState.hit.wasAddedToSelection = true; + this.scene.getElements(), + ); + }); + pointerDownState.hit.wasAddedToSelection = true; + } } } @@ -2571,6 +2639,29 @@ class App extends React.Component { return false; }; + private isASelectedElement(hitElement: ExcalidrawElement | null): boolean { + return hitElement != null && this.state.selectedElementIds[hitElement.id]; + } + + private isHittingCommonBoundingBoxOfSelectedElements( + point: Readonly<{ x: number; y: number }>, + selectedElements: readonly ExcalidrawElement[], + ): boolean { + if (selectedElements.length < 2) { + return false; + } + + // How many pixels off the shape boundary we still consider a hit + const threshold = 10 / this.state.zoom; + const [x1, y1, x2, y2] = getCommonBounds(selectedElements); + return ( + point.x > x1 - threshold && + point.x < x2 + threshold && + point.y > y1 - threshold && + point.y < y2 + threshold + ); + } + private handleTextOnPointerDown = ( event: React.PointerEvent, pointerDownState: PointerDownState, @@ -2852,8 +2943,13 @@ class App extends React.Component { } } - const hitElement = pointerDownState.hit.element; - if (hitElement && this.state.selectedElementIds[hitElement.id]) { + const hasHitASelectedElement = pointerDownState.hit.allHitElements.some( + (element) => this.isASelectedElement(element), + ); + if ( + hasHitASelectedElement || + pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements + ) { // Marking that click was used for dragging to check // if elements should be deselected on pointerup pointerDownState.drag.hasOccurred = true; @@ -2882,12 +2978,13 @@ class App extends React.Component { const elementsToAppend = []; const groupIdMap = new Map(); const oldIdToDuplicatedId = new Map(); + const hitElement = pointerDownState.hit.element; for (const element of this.scene.getElementsIncludingDeleted()) { if ( this.state.selectedElementIds[element.id] || // case: the state.selectedElementIds might not have been // updated yet by the time this mousemove event is fired - (element.id === hitElement.id && + (element.id === hitElement?.id && pointerDownState.hit.wasAddedToSelection) ) { const duplicatedElement = duplicateElement( @@ -3125,6 +3222,7 @@ class App extends React.Component { this.actionManager.executeAction(actionFinalize); return; } + if (isLinearElement(draggingElement)) { if (draggingElement!.points.length > 1) { history.resumeRecording(); @@ -3135,6 +3233,7 @@ class App extends React.Component { this.canvas, window.devicePixelRatio, ); + if ( !pointerDownState.drag.hasOccurred && draggingElement && @@ -3186,6 +3285,7 @@ class App extends React.Component { })); } } + return; } @@ -3230,35 +3330,111 @@ class App extends React.Component { ); } - // If click occurred on already selected element - // it is needed to remove selection from other elements - // or if SHIFT or META key pressed remove selection - // from hitted element - // - // If click occurred and elements were dragged or some element - // was added to selection (on pointerdown phase) we need to keep - // selection unchanged + // Code below handles selection when element(s) weren't + // drag or added to selection on pointer down phase. const hitElement = pointerDownState.hit.element; if ( - getSelectedGroupIds(this.state).length === 0 && hitElement && !pointerDownState.drag.hasOccurred && !pointerDownState.hit.wasAddedToSelection ) { if (childEvent.shiftKey) { - this.setState((prevState) => ({ - selectedElementIds: { - ...prevState.selectedElementIds, - [hitElement!.id]: false, - }, - })); + if (this.state.selectedElementIds[hitElement.id]) { + if (isSelectedViaGroup(this.state, hitElement)) { + // We want to unselect all groups hitElement is part of + // as well as all elements that are part of the groups + // hitElement is part of + const idsOfSelectedElementsThatAreInGroups = hitElement.groupIds + .flatMap((groupId) => + getElementsInGroup(this.scene.getElements(), groupId), + ) + .map((element) => ({ [element.id]: false })) + .reduce((prevId, acc) => ({ ...prevId, ...acc }), {}); + + this.setState((_prevState) => ({ + selectedGroupIds: { + ..._prevState.selectedElementIds, + ...hitElement.groupIds + .map((gId) => ({ [gId]: false })) + .reduce((prev, acc) => ({ ...prev, ...acc }), {}), + }, + selectedElementIds: { + ..._prevState.selectedElementIds, + ...idsOfSelectedElementsThatAreInGroups, + }, + })); + } else { + // remove element from selection while + // keeping prev elements selected + this.setState((prevState) => ({ + selectedElementIds: { + ...prevState.selectedElementIds, + [hitElement!.id]: false, + }, + })); + } + } else { + // add element to selection while + // keeping prev elements selected + this.setState((_prevState) => ({ + selectedElementIds: { + ..._prevState.selectedElementIds, + [hitElement!.id]: true, + }, + })); + } } else { - this.setState((_prevState) => ({ - selectedElementIds: { [hitElement!.id]: true }, - })); + if (isSelectedViaGroup(this.state, hitElement)) { + /* + We want to select the group(s) the hit element is in not the particular element. + That means we have to deselect elements that are not part of the groups of the + hit element, while keeping the elements that are. + */ + const idsOfSelectedElementsThatAreInGroups = hitElement.groupIds + .flatMap((groupId) => + getElementsInGroup(this.scene.getElements(), groupId), + ) + .map((element) => ({ [element.id]: true })) + .reduce((prevId, acc) => ({ ...prevId, ...acc }), {}); + + this.setState((_prevState) => ({ + selectedGroupIds: { + ...hitElement.groupIds + .map((gId) => ({ [gId]: true })) + .reduce((prevId, acc) => ({ ...prevId, ...acc }), {}), + }, + selectedElementIds: { ...idsOfSelectedElementsThatAreInGroups }, + })); + } else { + this.setState((_prevState) => ({ + selectedGroupIds: {}, + selectedElementIds: { [hitElement!.id]: true }, + })); + } } } + if ( + !this.state.editingLinearElement && + !pointerDownState.drag.hasOccurred && + !this.state.isResizing && + ((hitElement && + isHittingElementBoundingBoxWithoutHittingElement( + hitElement, + this.state, + pointerDownState.origin.x, + pointerDownState.origin.y, + )) || + (!hitElement && + pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements)) + ) { + // Deselect selected elements + this.setState({ + selectedElementIds: {}, + selectedGroupIds: {}, + }); + } + if (draggingElement === null) { // if no element is clicked, clear the selection and redraw this.setState({ @@ -3359,17 +3535,7 @@ class App extends React.Component { this.setState({ suggestedBindings }); } - private maybeClearSelectionWhenHittingElement( - event: React.PointerEvent, - hitElement: ExcalidrawElement | null, - ): void { - const isHittingASelectedElement = - hitElement != null && this.state.selectedElementIds[hitElement.id]; - - // clear selection if shift is not clicked - if (isHittingASelectedElement || event.shiftKey) { - return; - } + private clearSelection(hitElement: ExcalidrawElement | null): void { this.setState((prevState) => ({ selectedElementIds: {}, selectedGroupIds: {}, @@ -3713,5 +3879,4 @@ if ( }, }); } - export default App; diff --git a/src/constants.ts b/src/constants.ts index 90dd8c1c..27ba90c0 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -11,6 +11,8 @@ export const CURSOR_TYPE = { CROSSHAIR: "crosshair", GRABBING: "grabbing", POINTER: "pointer", + MOVE: "move", + AUTO: "", }; export const POINTER_BUTTON = { MAIN: 0, diff --git a/src/element/collision.ts b/src/element/collision.ts index 998ec61c..f4300e7f 100644 --- a/src/element/collision.ts +++ b/src/element/collision.ts @@ -48,9 +48,33 @@ export const hitTest = ( const point: Point = [x, y]; if (isElementSelected(appState, element)) { - return doesPointHitElementBoundingBox(element, point, threshold); + return isPointHittingElementBoundingBox(element, point, threshold); } + return isHittingElementNotConsideringBoundingBox(element, appState, point); +}; + +export const isHittingElementBoundingBoxWithoutHittingElement = ( + element: NonDeletedExcalidrawElement, + appState: AppState, + x: number, + y: number, +): boolean => { + const threshold = 10 / appState.zoom; + + return ( + !isHittingElementNotConsideringBoundingBox(element, appState, [x, y]) && + isPointHittingElementBoundingBox(element, [x, y], threshold) + ); +}; + +const isHittingElementNotConsideringBoundingBox = ( + element: NonDeletedExcalidrawElement, + appState: AppState, + point: Point, +): boolean => { + const threshold = 10 / appState.zoom; + const check = element.type === "text" ? isStrictlyInside @@ -65,7 +89,7 @@ const isElementSelected = ( element: NonDeleted, ) => appState.selectedElementIds[element.id]; -const doesPointHitElementBoundingBox = ( +const isPointHittingElementBoundingBox = ( element: NonDeleted, [x, y]: Point, threshold: number, diff --git a/src/element/index.ts b/src/element/index.ts index adf40178..3cc3ba35 100644 --- a/src/element/index.ts +++ b/src/element/index.ts @@ -26,7 +26,10 @@ export { getTransformHandlesFromCoords, getTransformHandles, } from "./transformHandles"; -export { hitTest } from "./collision"; +export { + hitTest, + isHittingElementBoundingBoxWithoutHittingElement, +} from "./collision"; export { resizeTest, getCursorForResizingElement, diff --git a/src/element/linearElementEditor.ts b/src/element/linearElementEditor.ts index 6e5ca19a..eaafc0e5 100644 --- a/src/element/linearElementEditor.ts +++ b/src/element/linearElementEditor.ts @@ -171,7 +171,7 @@ export class LinearElementEditor { scenePointer: { x: number; y: number }, ): { didAddPoint: boolean; - hitElement: ExcalidrawElement | null; + hitElement: NonDeleted | null; } { const ret: ReturnType = { didAddPoint: false, diff --git a/src/scene/comparisons.ts b/src/scene/comparisons.ts index 4067b1bf..e7e6ddd6 100644 --- a/src/scene/comparisons.ts +++ b/src/scene/comparisons.ts @@ -34,6 +34,8 @@ export const getElementAtPosition = ( ) => { let hitElement = null; // We need to to hit testing from front (end of the array) to back (beginning of the array) + // because array is ordered from lower z-index to highest and we want element z-index + // with higher z-index for (let i = elements.length - 1; i >= 0; --i) { const element = elements[i]; if (element.isDeleted) { @@ -48,6 +50,17 @@ export const getElementAtPosition = ( return hitElement; }; +export const getElementsAtPosition = ( + elements: readonly NonDeletedExcalidrawElement[], + isAtPositionFn: (element: NonDeletedExcalidrawElement) => boolean, +) => { + // The parameter elements comes ordered from lower z-index to higher. + // We want to preserve that order on the returned array. + return elements.filter( + (element) => !element.isDeleted && isAtPositionFn(element), + ); +}; + export const getElementContainingPosition = ( elements: readonly ExcalidrawElement[], x: number, diff --git a/src/scene/index.ts b/src/scene/index.ts index be18fd9d..dddeeeb6 100644 --- a/src/scene/index.ts +++ b/src/scene/index.ts @@ -14,5 +14,6 @@ export { getElementAtPosition, getElementContainingPosition, hasText, + getElementsAtPosition, } from "./comparisons"; export { getZoomOrigin, getNormalizedZoom } from "./zoom"; diff --git a/src/tests/__snapshots__/regressionTests.test.tsx.snap b/src/tests/__snapshots__/regressionTests.test.tsx.snap index ea2c9342..7e2e239e 100644 --- a/src/tests/__snapshots__/regressionTests.test.tsx.snap +++ b/src/tests/__snapshots__/regressionTests.test.tsx.snap @@ -1,5 +1,1060 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`given element A and group of elements B and given both are selected when user clicks on B, on pointer up only elements from B should be selected: [end of test] appState 1`] = ` +Object { + "appearance": "light", + "collaborators": Map {}, + "currentItemBackgroundColor": "transparent", + "currentItemFillStyle": "hachure", + "currentItemFontFamily": 1, + "currentItemFontSize": 20, + "currentItemLinearStrokeSharpness": "round", + "currentItemOpacity": 100, + "currentItemRoughness": 1, + "currentItemStrokeColor": "#000000", + "currentItemStrokeSharpness": "sharp", + "currentItemStrokeStyle": "solid", + "currentItemStrokeWidth": 1, + "currentItemTextAlign": "left", + "cursorButton": "up", + "cursorX": 0, + "cursorY": 0, + "draggingElement": null, + "editingElement": null, + "editingGroupId": null, + "editingLinearElement": null, + "elementLocked": false, + "elementType": "selection", + "errorMessage": null, + "exportBackground": true, + "gridSize": null, + "height": 768, + "isBindingEnabled": true, + "isCollaborating": false, + "isLibraryOpen": false, + "isLoading": false, + "isResizing": false, + "isRotating": false, + "lastPointerDownWith": "mouse", + "multiElement": null, + "name": "Untitled-201933152653", + "offsetLeft": 0, + "offsetTop": 0, + "openMenu": null, + "previousSelectedElementIds": Object { + "id0": true, + "id1": true, + "id2": true, + "id3": true, + "id5": true, + }, + "resizingElement": null, + "scrollX": 0, + "scrollY": 0, + "scrolledOutside": false, + "selectedElementIds": Object { + "id0": true, + "id2": true, + "id6": true, + }, + "selectedGroupIds": Object { + "id4": true, + }, + "selectionElement": null, + "shouldAddWatermark": false, + "shouldCacheIgnoreZoom": false, + "showShortcutsDialog": false, + "startBoundElement": null, + "suggestedBindings": Array [], + "username": "", + "viewBackgroundColor": "#ffffff", + "width": 1024, + "zenModeEnabled": false, + "zoom": 1, +} +`; + +exports[`given element A and group of elements B and given both are selected when user clicks on B, on pointer up only elements from B should be selected: [end of test] element 0 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 453191, + "width": 100, + "x": 110, + "y": 110, +} +`; + +exports[`given element A and group of elements B and given both are selected when user clicks on B, on pointer up only elements from B should be selected: [end of test] element 1 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id4", + ], + "height": 100, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 3, + "versionNonce": 1116226695, + "width": 100, + "x": 0, + "y": 0, +} +`; + +exports[`given element A and group of elements B and given both are selected when user clicks on B, on pointer up only elements from B should be selected: [end of test] element 2 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id4", + ], + "height": 100, + "id": "id2", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 401146281, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 3, + "versionNonce": 1014066025, + "width": 100, + "x": 220, + "y": 220, +} +`; + +exports[`given element A and group of elements B and given both are selected when user clicks on B, on pointer up only elements from B should be selected: [end of test] history 1`] = ` +Object { + "recording": false, + "redoStack": Array [], + "stateHistory": Array [ + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 100, + "x": 0, + "y": 0, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id1": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 100, + "x": 0, + "y": 0, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 453191, + "width": 100, + "x": 110, + "y": 110, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id2": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 100, + "x": 0, + "y": 0, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 453191, + "width": 100, + "x": 110, + "y": 110, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id2", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 401146281, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 2019559783, + "width": 100, + "x": 220, + "y": 220, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + "id2": true, + "id3": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 453191, + "width": 100, + "x": 110, + "y": 110, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id4", + ], + "height": 100, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 3, + "versionNonce": 1116226695, + "width": 100, + "x": 0, + "y": 0, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id4", + ], + "height": 100, + "id": "id2", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 401146281, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 3, + "versionNonce": 1014066025, + "width": 100, + "x": 220, + "y": 220, + }, + ], + }, + ], +} +`; + +exports[`given element A and group of elements B and given both are selected when user clicks on B, on pointer up only elements from B should be selected: [end of test] number of elements 1`] = `3`; + +exports[`given element A and group of elements B and given both are selected when user clicks on B, on pointer up only elements from B should be selected: [end of test] number of renders 1`] = `22`; + +exports[`given element A and group of elements B and given both are selected when user shift-clicks on B, on pointer up only element A should be selected: [end of test] appState 1`] = ` +Object { + "appearance": "light", + "collaborators": Map {}, + "currentItemBackgroundColor": "transparent", + "currentItemFillStyle": "hachure", + "currentItemFontFamily": 1, + "currentItemFontSize": 20, + "currentItemLinearStrokeSharpness": "round", + "currentItemOpacity": 100, + "currentItemRoughness": 1, + "currentItemStrokeColor": "#000000", + "currentItemStrokeSharpness": "sharp", + "currentItemStrokeStyle": "solid", + "currentItemStrokeWidth": 1, + "currentItemTextAlign": "left", + "cursorButton": "up", + "cursorX": 0, + "cursorY": 0, + "draggingElement": null, + "editingElement": null, + "editingGroupId": null, + "editingLinearElement": null, + "elementLocked": false, + "elementType": "selection", + "errorMessage": null, + "exportBackground": true, + "gridSize": null, + "height": 768, + "isBindingEnabled": true, + "isCollaborating": false, + "isLibraryOpen": false, + "isLoading": false, + "isResizing": false, + "isRotating": false, + "lastPointerDownWith": "mouse", + "multiElement": null, + "name": "Untitled-201933152653", + "offsetLeft": 0, + "offsetTop": 0, + "openMenu": null, + "previousSelectedElementIds": Object { + "id0": true, + "id1": true, + "id2": true, + "id3": true, + "id5": true, + }, + "resizingElement": null, + "scrollX": 0, + "scrollY": 0, + "scrolledOutside": false, + "selectedElementIds": Object { + "id0": false, + "id1": true, + "id2": false, + "id3": true, + "id5": true, + "id6": true, + }, + "selectedGroupIds": Object { + "id0": true, + "id1": true, + "id2": true, + "id3": true, + "id4": false, + "id5": true, + }, + "selectionElement": null, + "shouldAddWatermark": false, + "shouldCacheIgnoreZoom": false, + "showShortcutsDialog": false, + "startBoundElement": null, + "suggestedBindings": Array [], + "username": "", + "viewBackgroundColor": "#ffffff", + "width": 1024, + "zenModeEnabled": false, + "zoom": 1, +} +`; + +exports[`given element A and group of elements B and given both are selected when user shift-clicks on B, on pointer up only element A should be selected: [end of test] element 0 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 453191, + "width": 100, + "x": 110, + "y": 110, +} +`; + +exports[`given element A and group of elements B and given both are selected when user shift-clicks on B, on pointer up only element A should be selected: [end of test] element 1 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id4", + ], + "height": 100, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 3, + "versionNonce": 1116226695, + "width": 100, + "x": 0, + "y": 0, +} +`; + +exports[`given element A and group of elements B and given both are selected when user shift-clicks on B, on pointer up only element A should be selected: [end of test] element 2 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id4", + ], + "height": 100, + "id": "id2", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 401146281, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 3, + "versionNonce": 1014066025, + "width": 100, + "x": 220, + "y": 220, +} +`; + +exports[`given element A and group of elements B and given both are selected when user shift-clicks on B, on pointer up only element A should be selected: [end of test] history 1`] = ` +Object { + "recording": false, + "redoStack": Array [], + "stateHistory": Array [ + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 100, + "x": 0, + "y": 0, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id1": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 100, + "x": 0, + "y": 0, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 453191, + "width": 100, + "x": 110, + "y": 110, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id2": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 100, + "x": 0, + "y": 0, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 453191, + "width": 100, + "x": 110, + "y": 110, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id2", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 401146281, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 2019559783, + "width": 100, + "x": 220, + "y": 220, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + "id2": true, + "id3": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 453191, + "width": 100, + "x": 110, + "y": 110, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id4", + ], + "height": 100, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 3, + "versionNonce": 1116226695, + "width": 100, + "x": 0, + "y": 0, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id4", + ], + "height": 100, + "id": "id2", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 401146281, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 3, + "versionNonce": 1014066025, + "width": 100, + "x": 220, + "y": 220, + }, + ], + }, + ], +} +`; + +exports[`given element A and group of elements B and given both are selected when user shift-clicks on B, on pointer up only element A should be selected: [end of test] number of elements 1`] = `3`; + +exports[`given element A and group of elements B and given both are selected when user shift-clicks on B, on pointer up only element A should be selected: [end of test] number of renders 1`] = `22`; + +exports[`regression tests Drags selected element when hitting only bounding box and keeps element selected: [end of test] appState 1`] = ` +Object { + "appearance": "light", + "collaborators": Map {}, + "currentItemBackgroundColor": "transparent", + "currentItemFillStyle": "hachure", + "currentItemFontFamily": 1, + "currentItemFontSize": 20, + "currentItemLinearStrokeSharpness": "round", + "currentItemOpacity": 100, + "currentItemRoughness": 1, + "currentItemStrokeColor": "#000000", + "currentItemStrokeSharpness": "sharp", + "currentItemStrokeStyle": "solid", + "currentItemStrokeWidth": 1, + "currentItemTextAlign": "left", + "cursorButton": "up", + "cursorX": 0, + "cursorY": 0, + "draggingElement": null, + "editingElement": null, + "editingGroupId": null, + "editingLinearElement": null, + "elementLocked": false, + "elementType": "selection", + "errorMessage": null, + "exportBackground": true, + "gridSize": null, + "height": 768, + "isBindingEnabled": true, + "isCollaborating": false, + "isLibraryOpen": false, + "isLoading": false, + "isResizing": false, + "isRotating": false, + "lastPointerDownWith": "mouse", + "multiElement": null, + "name": "Untitled-201933152653", + "offsetLeft": 0, + "offsetTop": 0, + "openMenu": null, + "previousSelectedElementIds": Object { + "id0": true, + }, + "resizingElement": null, + "scrollX": 0, + "scrollY": 0, + "scrolledOutside": false, + "selectedElementIds": Object { + "id0": true, + "id1": true, + }, + "selectedGroupIds": Object {}, + "selectionElement": null, + "shouldAddWatermark": false, + "shouldCacheIgnoreZoom": false, + "showShortcutsDialog": false, + "startBoundElement": null, + "suggestedBindings": Array [], + "username": "", + "viewBackgroundColor": "#ffffff", + "width": 1024, + "zenModeEnabled": false, + "zoom": 1, +} +`; + +exports[`regression tests Drags selected element when hitting only bounding box and keeps element selected: [end of test] element 0 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 3, + "versionNonce": 453191, + "width": 10, + "x": 25, + "y": 25, +} +`; + +exports[`regression tests Drags selected element when hitting only bounding box and keeps element selected: [end of test] history 1`] = ` +Object { + "recording": false, + "redoStack": Array [], + "stateHistory": Array [ + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + "id1": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 3, + "versionNonce": 453191, + "width": 10, + "x": 25, + "y": 25, + }, + ], + }, + ], +} +`; + +exports[`regression tests Drags selected element when hitting only bounding box and keeps element selected: [end of test] number of elements 1`] = `1`; + +exports[`regression tests Drags selected element when hitting only bounding box and keeps element selected: [end of test] number of renders 1`] = `10`; + exports[`regression tests adjusts z order when grouping: [end of test] appState 1`] = ` Object { "appearance": "light", @@ -800,6 +1855,458 @@ exports[`regression tests arrow keys: [end of test] number of elements 1`] = `1` exports[`regression tests arrow keys: [end of test] number of renders 1`] = `13`; +exports[`regression tests can drag element that covers another element, while another elem is selected: [end of test] appState 1`] = ` +Object { + "appearance": "light", + "collaborators": Map {}, + "currentItemBackgroundColor": "transparent", + "currentItemFillStyle": "hachure", + "currentItemFontFamily": 1, + "currentItemFontSize": 20, + "currentItemLinearStrokeSharpness": "round", + "currentItemOpacity": 100, + "currentItemRoughness": 1, + "currentItemStrokeColor": "#000000", + "currentItemStrokeSharpness": "sharp", + "currentItemStrokeStyle": "solid", + "currentItemStrokeWidth": 1, + "currentItemTextAlign": "left", + "cursorButton": "up", + "cursorX": 0, + "cursorY": 0, + "draggingElement": null, + "editingElement": null, + "editingGroupId": null, + "editingLinearElement": null, + "elementLocked": false, + "elementType": "selection", + "errorMessage": null, + "exportBackground": true, + "gridSize": null, + "height": 768, + "isBindingEnabled": true, + "isCollaborating": false, + "isLibraryOpen": false, + "isLoading": false, + "isResizing": false, + "isRotating": false, + "lastPointerDownWith": "mouse", + "multiElement": null, + "name": "Untitled-201933152653", + "offsetLeft": 0, + "offsetTop": 0, + "openMenu": null, + "previousSelectedElementIds": Object { + "id2": true, + }, + "resizingElement": null, + "scrollX": 0, + "scrollY": 0, + "scrolledOutside": false, + "selectedElementIds": Object { + "id1": true, + "id3": true, + }, + "selectedGroupIds": Object {}, + "selectionElement": null, + "shouldAddWatermark": false, + "shouldCacheIgnoreZoom": false, + "showShortcutsDialog": false, + "startBoundElement": null, + "suggestedBindings": Array [], + "username": "", + "viewBackgroundColor": "#ffffff", + "width": 1024, + "zenModeEnabled": false, + "zoom": 1, +} +`; + +exports[`regression tests can drag element that covers another element, while another elem is selected: [end of test] element 0 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 200, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 1278240551, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 449462985, + "width": 200, + "x": 100, + "y": 100, +} +`; + +exports[`regression tests can drag element that covers another element, while another elem is selected: [end of test] element 1 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 200, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 453191, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 3, + "versionNonce": 1014066025, + "width": 200, + "x": 300, + "y": 300, +} +`; + +exports[`regression tests can drag element that covers another element, while another elem is selected: [end of test] element 2 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 350, + "id": "id2", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 2019559783, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 1150084233, + "width": 350, + "x": 300, + "y": 300, +} +`; + +exports[`regression tests can drag element that covers another element, while another elem is selected: [end of test] history 1`] = ` +Object { + "recording": false, + "redoStack": Array [], + "stateHistory": Array [ + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object {}, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 0, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 1278240551, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 1, + "versionNonce": 0, + "width": 0, + "x": 100, + "y": 100, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 200, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 1278240551, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 449462985, + "width": 200, + "x": 100, + "y": 100, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id1": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 200, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 1278240551, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 449462985, + "width": 200, + "x": 100, + "y": 100, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 200, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 453191, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 401146281, + "width": 200, + "x": 100, + "y": 100, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id2": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 200, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 1278240551, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 449462985, + "width": 200, + "x": 100, + "y": 100, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 200, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 453191, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 401146281, + "width": 200, + "x": 100, + "y": 100, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 350, + "id": "id2", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 2019559783, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 1150084233, + "width": 350, + "x": 300, + "y": 300, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id1": true, + "id3": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 200, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 1278240551, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 449462985, + "width": 200, + "x": 100, + "y": 100, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 200, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 453191, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 3, + "versionNonce": 1014066025, + "width": 200, + "x": 300, + "y": 300, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 350, + "id": "id2", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 2019559783, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 1150084233, + "width": 350, + "x": 300, + "y": 300, + }, + ], + }, + ], +} +`; + +exports[`regression tests can drag element that covers another element, while another elem is selected: [end of test] number of elements 1`] = `3`; + +exports[`regression tests can drag element that covers another element, while another elem is selected: [end of test] number of renders 1`] = `18`; + exports[`regression tests change the properties of a shape: [end of test] appState 1`] = ` Object { "appearance": "light", @@ -2044,6 +3551,862 @@ exports[`regression tests click-drag to select a group: [end of test] number of exports[`regression tests click-drag to select a group: [end of test] number of renders 1`] = `19`; +exports[`regression tests deselects group of selected elements on pointer down when pointer doesn't hit any element: [end of test] appState 1`] = ` +Object { + "appearance": "light", + "collaborators": Map {}, + "currentItemBackgroundColor": "transparent", + "currentItemFillStyle": "hachure", + "currentItemFontFamily": 1, + "currentItemFontSize": 20, + "currentItemLinearStrokeSharpness": "round", + "currentItemOpacity": 100, + "currentItemRoughness": 1, + "currentItemStrokeColor": "#000000", + "currentItemStrokeSharpness": "sharp", + "currentItemStrokeStyle": "solid", + "currentItemStrokeWidth": 1, + "currentItemTextAlign": "left", + "cursorButton": "down", + "cursorX": 0, + "cursorY": 0, + "draggingElement": Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 0, + "id": "id3", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 2019559783, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "selection", + "version": 1, + "versionNonce": 0, + "width": 0, + "x": 500, + "y": 500, + }, + "editingElement": null, + "editingGroupId": null, + "editingLinearElement": null, + "elementLocked": false, + "elementType": "selection", + "errorMessage": null, + "exportBackground": true, + "gridSize": null, + "height": 768, + "isBindingEnabled": true, + "isCollaborating": false, + "isLibraryOpen": false, + "isLoading": false, + "isResizing": false, + "isRotating": false, + "lastPointerDownWith": "mouse", + "multiElement": null, + "name": "Untitled-201933152653", + "offsetLeft": 0, + "offsetTop": 0, + "openMenu": null, + "previousSelectedElementIds": Object { + "id0": true, + "id1": true, + "id2": true, + }, + "resizingElement": null, + "scrollX": 0, + "scrollY": 0, + "scrolledOutside": false, + "selectedElementIds": Object {}, + "selectedGroupIds": Object {}, + "selectionElement": Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 0, + "id": "id3", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 2019559783, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "selection", + "version": 1, + "versionNonce": 0, + "width": 0, + "x": 500, + "y": 500, + }, + "shouldAddWatermark": false, + "shouldCacheIgnoreZoom": false, + "showShortcutsDialog": false, + "startBoundElement": null, + "suggestedBindings": Array [], + "username": "", + "viewBackgroundColor": "#ffffff", + "width": 1024, + "zenModeEnabled": false, + "zoom": 1, +} +`; + +exports[`regression tests deselects group of selected elements on pointer down when pointer doesn't hit any element: [end of test] element 0 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, +} +`; + +exports[`regression tests deselects group of selected elements on pointer down when pointer doesn't hit any element: [end of test] element 1 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 453191, + "width": 10, + "x": 110, + "y": 110, +} +`; + +exports[`regression tests deselects group of selected elements on pointer down when pointer doesn't hit any element: [end of test] history 1`] = ` +Object { + "recording": false, + "redoStack": Array [], + "stateHistory": Array [ + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id1": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 453191, + "width": 10, + "x": 110, + "y": 110, + }, + ], + }, + ], +} +`; + +exports[`regression tests deselects group of selected elements on pointer down when pointer doesn't hit any element: [end of test] number of elements 1`] = `2`; + +exports[`regression tests deselects group of selected elements on pointer down when pointer doesn't hit any element: [end of test] number of renders 1`] = `14`; + +exports[`regression tests deselects group of selected elements on pointer up when pointer hits common bounding box without hitting any element: [end of test] appState 1`] = ` +Object { + "appearance": "light", + "collaborators": Map {}, + "currentItemBackgroundColor": "transparent", + "currentItemFillStyle": "hachure", + "currentItemFontFamily": 1, + "currentItemFontSize": 20, + "currentItemLinearStrokeSharpness": "round", + "currentItemOpacity": 100, + "currentItemRoughness": 1, + "currentItemStrokeColor": "#000000", + "currentItemStrokeSharpness": "sharp", + "currentItemStrokeStyle": "solid", + "currentItemStrokeWidth": 1, + "currentItemTextAlign": "left", + "cursorButton": "up", + "cursorX": 0, + "cursorY": 0, + "draggingElement": null, + "editingElement": null, + "editingGroupId": null, + "editingLinearElement": null, + "elementLocked": false, + "elementType": "selection", + "errorMessage": null, + "exportBackground": true, + "gridSize": null, + "height": 768, + "isBindingEnabled": true, + "isCollaborating": false, + "isLibraryOpen": false, + "isLoading": false, + "isResizing": false, + "isRotating": false, + "lastPointerDownWith": "mouse", + "multiElement": null, + "name": "Untitled-201933152653", + "offsetLeft": 0, + "offsetTop": 0, + "openMenu": null, + "previousSelectedElementIds": Object { + "id0": true, + "id1": true, + "id2": true, + }, + "resizingElement": null, + "scrollX": 0, + "scrollY": 0, + "scrolledOutside": false, + "selectedElementIds": Object { + "id3": true, + }, + "selectedGroupIds": Object {}, + "selectionElement": null, + "shouldAddWatermark": false, + "shouldCacheIgnoreZoom": false, + "showShortcutsDialog": false, + "startBoundElement": null, + "suggestedBindings": Array [], + "username": "", + "viewBackgroundColor": "#ffffff", + "width": 1024, + "zenModeEnabled": false, + "zoom": 1, +} +`; + +exports[`regression tests deselects group of selected elements on pointer up when pointer hits common bounding box without hitting any element: [end of test] element 0 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, +} +`; + +exports[`regression tests deselects group of selected elements on pointer up when pointer hits common bounding box without hitting any element: [end of test] element 1 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 453191, + "width": 10, + "x": 110, + "y": 110, +} +`; + +exports[`regression tests deselects group of selected elements on pointer up when pointer hits common bounding box without hitting any element: [end of test] history 1`] = ` +Object { + "recording": false, + "redoStack": Array [], + "stateHistory": Array [ + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object {}, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 0, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 1, + "versionNonce": 0, + "width": 0, + "x": 0, + "y": 0, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id1": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 453191, + "width": 10, + "x": 110, + "y": 110, + }, + ], + }, + ], +} +`; + +exports[`regression tests deselects group of selected elements on pointer up when pointer hits common bounding box without hitting any element: [end of test] number of elements 1`] = `2`; + +exports[`regression tests deselects group of selected elements on pointer up when pointer hits common bounding box without hitting any element: [end of test] number of renders 1`] = `15`; + +exports[`regression tests deselects selected element on pointer down when pointer doesn't hit any element: [end of test] appState 1`] = ` +Object { + "appearance": "light", + "collaborators": Map {}, + "currentItemBackgroundColor": "transparent", + "currentItemFillStyle": "hachure", + "currentItemFontFamily": 1, + "currentItemFontSize": 20, + "currentItemLinearStrokeSharpness": "round", + "currentItemOpacity": 100, + "currentItemRoughness": 1, + "currentItemStrokeColor": "#000000", + "currentItemStrokeSharpness": "sharp", + "currentItemStrokeStyle": "solid", + "currentItemStrokeWidth": 1, + "currentItemTextAlign": "left", + "cursorButton": "down", + "cursorX": 0, + "cursorY": 0, + "draggingElement": Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 0, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "selection", + "version": 1, + "versionNonce": 0, + "width": 0, + "x": 110, + "y": 110, + }, + "editingElement": null, + "editingGroupId": null, + "editingLinearElement": null, + "elementLocked": false, + "elementType": "selection", + "errorMessage": null, + "exportBackground": true, + "gridSize": null, + "height": 768, + "isBindingEnabled": true, + "isCollaborating": false, + "isLibraryOpen": false, + "isLoading": false, + "isResizing": false, + "isRotating": false, + "lastPointerDownWith": "mouse", + "multiElement": null, + "name": "Untitled-201933152653", + "offsetLeft": 0, + "offsetTop": 0, + "openMenu": null, + "previousSelectedElementIds": Object { + "id0": true, + }, + "resizingElement": null, + "scrollX": 0, + "scrollY": 0, + "scrolledOutside": false, + "selectedElementIds": Object {}, + "selectedGroupIds": Object {}, + "selectionElement": Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 0, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "selection", + "version": 1, + "versionNonce": 0, + "width": 0, + "x": 110, + "y": 110, + }, + "shouldAddWatermark": false, + "shouldCacheIgnoreZoom": false, + "showShortcutsDialog": false, + "startBoundElement": null, + "suggestedBindings": Array [], + "username": "", + "viewBackgroundColor": "#ffffff", + "width": 1024, + "zenModeEnabled": false, + "zoom": 1, +} +`; + +exports[`regression tests deselects selected element on pointer down when pointer doesn't hit any element: [end of test] element 0 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, +} +`; + +exports[`regression tests deselects selected element on pointer down when pointer doesn't hit any element: [end of test] history 1`] = ` +Object { + "recording": false, + "redoStack": Array [], + "stateHistory": Array [ + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, + }, + ], + }, + ], +} +`; + +exports[`regression tests deselects selected element on pointer down when pointer doesn't hit any element: [end of test] number of elements 1`] = `1`; + +exports[`regression tests deselects selected element on pointer down when pointer doesn't hit any element: [end of test] number of renders 1`] = `8`; + +exports[`regression tests deselects selected element, on pointer up, when click hits element bounding box but doesn't hit the element: [end of test] appState 1`] = ` +Object { + "appearance": "light", + "collaborators": Map {}, + "currentItemBackgroundColor": "transparent", + "currentItemFillStyle": "hachure", + "currentItemFontFamily": 1, + "currentItemFontSize": 20, + "currentItemLinearStrokeSharpness": "round", + "currentItemOpacity": 100, + "currentItemRoughness": 1, + "currentItemStrokeColor": "#000000", + "currentItemStrokeSharpness": "sharp", + "currentItemStrokeStyle": "solid", + "currentItemStrokeWidth": 1, + "currentItemTextAlign": "left", + "cursorButton": "up", + "cursorX": 0, + "cursorY": 0, + "draggingElement": null, + "editingElement": null, + "editingGroupId": null, + "editingLinearElement": null, + "elementLocked": false, + "elementType": "selection", + "errorMessage": null, + "exportBackground": true, + "gridSize": null, + "height": 768, + "isBindingEnabled": true, + "isCollaborating": false, + "isLibraryOpen": false, + "isLoading": false, + "isResizing": false, + "isRotating": false, + "lastPointerDownWith": "mouse", + "multiElement": null, + "name": "Untitled-201933152653", + "offsetLeft": 0, + "offsetTop": 0, + "openMenu": null, + "previousSelectedElementIds": Object { + "id0": true, + }, + "resizingElement": null, + "scrollX": 0, + "scrollY": 0, + "scrolledOutside": false, + "selectedElementIds": Object { + "id1": true, + }, + "selectedGroupIds": Object {}, + "selectionElement": null, + "shouldAddWatermark": false, + "shouldCacheIgnoreZoom": false, + "showShortcutsDialog": false, + "startBoundElement": null, + "suggestedBindings": Array [], + "username": "", + "viewBackgroundColor": "#ffffff", + "width": 1024, + "zenModeEnabled": false, + "zoom": 1, +} +`; + +exports[`regression tests deselects selected element, on pointer up, when click hits element bounding box but doesn't hit the element: [end of test] element 0 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 1278240551, + "width": 100, + "x": 0, + "y": 0, +} +`; + +exports[`regression tests deselects selected element, on pointer up, when click hits element bounding box but doesn't hit the element: [end of test] history 1`] = ` +Object { + "recording": false, + "redoStack": Array [], + "stateHistory": Array [ + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 1278240551, + "width": 100, + "x": 0, + "y": 0, + }, + ], + }, + ], +} +`; + +exports[`regression tests deselects selected element, on pointer up, when click hits element bounding box but doesn't hit the element: [end of test] number of elements 1`] = `1`; + +exports[`regression tests deselects selected element, on pointer up, when click hits element bounding box but doesn't hit the element: [end of test] number of renders 1`] = `9`; + exports[`regression tests double click to edit a group: [end of test] appState 1`] = ` Object { "appearance": "light", @@ -2472,6 +4835,299 @@ exports[`regression tests double click to edit a group: [end of test] number of exports[`regression tests double click to edit a group: [end of test] number of renders 1`] = `18`; +exports[`regression tests drags selected elements from point inside common bounding box that doesn't hit any element and keeps elements selected after dragging: [end of test] appState 1`] = ` +Object { + "appearance": "light", + "collaborators": Map {}, + "currentItemBackgroundColor": "transparent", + "currentItemFillStyle": "hachure", + "currentItemFontFamily": 1, + "currentItemFontSize": 20, + "currentItemLinearStrokeSharpness": "round", + "currentItemOpacity": 100, + "currentItemRoughness": 1, + "currentItemStrokeColor": "#000000", + "currentItemStrokeSharpness": "sharp", + "currentItemStrokeStyle": "solid", + "currentItemStrokeWidth": 1, + "currentItemTextAlign": "left", + "cursorButton": "up", + "cursorX": 0, + "cursorY": 0, + "draggingElement": null, + "editingElement": null, + "editingGroupId": null, + "editingLinearElement": null, + "elementLocked": false, + "elementType": "selection", + "errorMessage": null, + "exportBackground": true, + "gridSize": null, + "height": 768, + "isBindingEnabled": true, + "isCollaborating": false, + "isLibraryOpen": false, + "isLoading": false, + "isResizing": false, + "isRotating": false, + "lastPointerDownWith": "mouse", + "multiElement": null, + "name": "Untitled-201933152653", + "offsetLeft": 0, + "offsetTop": 0, + "openMenu": null, + "previousSelectedElementIds": Object { + "id0": true, + "id1": true, + "id2": true, + }, + "resizingElement": null, + "scrollX": 0, + "scrollY": 0, + "scrolledOutside": false, + "selectedElementIds": Object { + "id0": true, + "id1": true, + "id2": true, + "id3": true, + }, + "selectedGroupIds": Object {}, + "selectionElement": null, + "shouldAddWatermark": false, + "shouldCacheIgnoreZoom": false, + "showShortcutsDialog": false, + "startBoundElement": null, + "suggestedBindings": Array [], + "username": "", + "viewBackgroundColor": "#ffffff", + "width": 1024, + "zenModeEnabled": false, + "zoom": 1, +} +`; + +exports[`regression tests drags selected elements from point inside common bounding box that doesn't hit any element and keeps elements selected after dragging: [end of test] element 0 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 3, + "versionNonce": 1150084233, + "width": 10, + "x": 25, + "y": 25, +} +`; + +exports[`regression tests drags selected elements from point inside common bounding box that doesn't hit any element and keeps elements selected after dragging: [end of test] element 1 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 3, + "versionNonce": 1116226695, + "width": 10, + "x": 135, + "y": 135, +} +`; + +exports[`regression tests drags selected elements from point inside common bounding box that doesn't hit any element and keeps elements selected after dragging: [end of test] history 1`] = ` +Object { + "recording": false, + "redoStack": Array [], + "stateHistory": Array [ + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id1": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 453191, + "width": 10, + "x": 110, + "y": 110, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + "id1": true, + "id2": true, + "id3": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 3, + "versionNonce": 1150084233, + "width": 10, + "x": 25, + "y": 25, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 3, + "versionNonce": 1116226695, + "width": 10, + "x": 135, + "y": 135, + }, + ], + }, + ], +} +`; + +exports[`regression tests drags selected elements from point inside common bounding box that doesn't hit any element and keeps elements selected after dragging: [end of test] number of elements 1`] = `2`; + +exports[`regression tests drags selected elements from point inside common bounding box that doesn't hit any element and keeps elements selected after dragging: [end of test] number of renders 1`] = `16`; + exports[`regression tests draw every type of shape: [end of test] appState 1`] = ` Object { "appearance": "light", @@ -4437,6 +7093,1121 @@ exports[`regression tests draw every type of shape: [end of test] number of elem exports[`regression tests draw every type of shape: [end of test] number of renders 1`] = `51`; +exports[`regression tests given a group of selected elements with an element that is not selected inside the group common bounding box when element that is not selected is clicked should switch selection to not selected element on pointer up: [end of test] appState 1`] = ` +Object { + "appearance": "light", + "collaborators": Map {}, + "currentItemBackgroundColor": "transparent", + "currentItemFillStyle": "hachure", + "currentItemFontFamily": 1, + "currentItemFontSize": 20, + "currentItemLinearStrokeSharpness": "round", + "currentItemOpacity": 100, + "currentItemRoughness": 1, + "currentItemStrokeColor": "#000000", + "currentItemStrokeSharpness": "sharp", + "currentItemStrokeStyle": "solid", + "currentItemStrokeWidth": 1, + "currentItemTextAlign": "left", + "cursorButton": "up", + "cursorX": 0, + "cursorY": 0, + "draggingElement": null, + "editingElement": null, + "editingGroupId": null, + "editingLinearElement": null, + "elementLocked": false, + "elementType": "selection", + "errorMessage": null, + "exportBackground": true, + "gridSize": null, + "height": 768, + "isBindingEnabled": true, + "isCollaborating": false, + "isLibraryOpen": false, + "isLoading": false, + "isResizing": false, + "isRotating": false, + "lastPointerDownWith": "mouse", + "multiElement": null, + "name": "Untitled-201933152653", + "offsetLeft": 0, + "offsetTop": 0, + "openMenu": null, + "previousSelectedElementIds": Object { + "id0": true, + "id2": true, + "id3": true, + }, + "resizingElement": null, + "scrollX": 0, + "scrollY": 0, + "scrolledOutside": false, + "selectedElementIds": Object { + "id1": true, + "id4": true, + }, + "selectedGroupIds": Object {}, + "selectionElement": null, + "shouldAddWatermark": false, + "shouldCacheIgnoreZoom": false, + "showShortcutsDialog": false, + "startBoundElement": null, + "suggestedBindings": Array [], + "username": "", + "viewBackgroundColor": "#ffffff", + "width": 1024, + "zenModeEnabled": false, + "zoom": 1, +} +`; + +exports[`regression tests given a group of selected elements with an element that is not selected inside the group common bounding box when element that is not selected is clicked should switch selection to not selected element on pointer up: [end of test] element 0 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, +} +`; + +exports[`regression tests given a group of selected elements with an element that is not selected inside the group common bounding box when element that is not selected is clicked should switch selection to not selected element on pointer up: [end of test] element 1 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 453191, + "width": 100, + "x": 110, + "y": 110, +} +`; + +exports[`regression tests given a group of selected elements with an element that is not selected inside the group common bounding box when element that is not selected is clicked should switch selection to not selected element on pointer up: [end of test] element 2 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id2", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 401146281, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "diamond", + "version": 2, + "versionNonce": 2019559783, + "width": 100, + "x": 310, + "y": 310, +} +`; + +exports[`regression tests given a group of selected elements with an element that is not selected inside the group common bounding box when element that is not selected is clicked should switch selection to not selected element on pointer up: [end of test] history 1`] = ` +Object { + "recording": false, + "redoStack": Array [], + "stateHistory": Array [ + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id1": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 453191, + "width": 100, + "x": 110, + "y": 110, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id2": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 453191, + "width": 100, + "x": 110, + "y": 110, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id2", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 401146281, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "diamond", + "version": 2, + "versionNonce": 2019559783, + "width": 100, + "x": 310, + "y": 310, + }, + ], + }, + ], +} +`; + +exports[`regression tests given a group of selected elements with an element that is not selected inside the group common bounding box when element that is not selected is clicked should switch selection to not selected element on pointer up: [end of test] number of elements 1`] = `3`; + +exports[`regression tests given a group of selected elements with an element that is not selected inside the group common bounding box when element that is not selected is clicked should switch selection to not selected element on pointer up: [end of test] number of renders 1`] = `19`; + +exports[`regression tests given a selected element A and a not selected element B with higher z-index than A and given B partialy overlaps A when there's a shift-click on the overlapped section B is added to the selection: [end of test] appState 1`] = ` +Object { + "appearance": "light", + "collaborators": Map {}, + "currentItemBackgroundColor": "#fa5252", + "currentItemFillStyle": "hachure", + "currentItemFontFamily": 1, + "currentItemFontSize": 20, + "currentItemLinearStrokeSharpness": "round", + "currentItemOpacity": 100, + "currentItemRoughness": 1, + "currentItemStrokeColor": "#000000", + "currentItemStrokeSharpness": "sharp", + "currentItemStrokeStyle": "solid", + "currentItemStrokeWidth": 1, + "currentItemTextAlign": "left", + "cursorButton": "up", + "cursorX": 0, + "cursorY": 0, + "draggingElement": null, + "editingElement": null, + "editingGroupId": null, + "editingLinearElement": null, + "elementLocked": false, + "elementType": "selection", + "errorMessage": null, + "exportBackground": true, + "gridSize": null, + "height": 768, + "isBindingEnabled": true, + "isCollaborating": false, + "isLibraryOpen": false, + "isLoading": false, + "isResizing": false, + "isRotating": false, + "lastPointerDownWith": "mouse", + "multiElement": null, + "name": "Untitled-201933152653", + "offsetLeft": 0, + "offsetTop": 0, + "openMenu": null, + "previousSelectedElementIds": Object { + "id0": true, + "id2": true, + }, + "resizingElement": null, + "scrollX": 0, + "scrollY": 0, + "scrolledOutside": false, + "selectedElementIds": Object { + "id0": true, + "id1": true, + "id2": true, + "id3": true, + }, + "selectedGroupIds": Object {}, + "selectionElement": null, + "shouldAddWatermark": false, + "shouldCacheIgnoreZoom": false, + "showShortcutsDialog": false, + "startBoundElement": null, + "suggestedBindings": Array [], + "username": "", + "viewBackgroundColor": "#ffffff", + "width": 1024, + "zenModeEnabled": false, + "zoom": 1, +} +`; + +exports[`regression tests given a selected element A and a not selected element B with higher z-index than A and given B partialy overlaps A when there's a shift-click on the overlapped section B is added to the selection: [end of test] element 0 1`] = ` +Object { + "angle": 0, + "backgroundColor": "#fa5252", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 1000, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 1000, + "x": 0, + "y": 0, +} +`; + +exports[`regression tests given a selected element A and a not selected element B with higher z-index than A and given B partialy overlaps A when there's a shift-click on the overlapped section B is added to the selection: [end of test] element 1 1`] = ` +Object { + "angle": 0, + "backgroundColor": "#fa5252", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 1000, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 453191, + "width": 1000, + "x": 500, + "y": 500, +} +`; + +exports[`regression tests given a selected element A and a not selected element B with higher z-index than A and given B partialy overlaps A when there's a shift-click on the overlapped section B is added to the selection: [end of test] history 1`] = ` +Object { + "recording": false, + "redoStack": Array [], + "stateHistory": Array [ + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object {}, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "#fa5252", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 1000, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 1000, + "x": 0, + "y": 0, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id1": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "#fa5252", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 1000, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 1000, + "x": 0, + "y": 0, + }, + Object { + "angle": 0, + "backgroundColor": "#fa5252", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 1000, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 453191, + "width": 1000, + "x": 500, + "y": 500, + }, + ], + }, + ], +} +`; + +exports[`regression tests given a selected element A and a not selected element B with higher z-index than A and given B partialy overlaps A when there's a shift-click on the overlapped section B is added to the selection: [end of test] number of elements 1`] = `2`; + +exports[`regression tests given a selected element A and a not selected element B with higher z-index than A and given B partialy overlaps A when there's a shift-click on the overlapped section B is added to the selection: [end of test] number of renders 1`] = `17`; + +exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when clicking intersection between A and B B should be selected on pointer up: [end of test] appState 1`] = ` +Object { + "appearance": "light", + "collaborators": Map {}, + "currentItemBackgroundColor": "#fa5252", + "currentItemFillStyle": "hachure", + "currentItemFontFamily": 1, + "currentItemFontSize": 20, + "currentItemLinearStrokeSharpness": "round", + "currentItemOpacity": 100, + "currentItemRoughness": 1, + "currentItemStrokeColor": "#000000", + "currentItemStrokeSharpness": "sharp", + "currentItemStrokeStyle": "solid", + "currentItemStrokeWidth": 1, + "currentItemTextAlign": "left", + "cursorButton": "up", + "cursorX": 0, + "cursorY": 0, + "draggingElement": null, + "editingElement": null, + "editingGroupId": null, + "editingLinearElement": null, + "elementLocked": false, + "elementType": "selection", + "errorMessage": null, + "exportBackground": true, + "gridSize": null, + "height": 768, + "isBindingEnabled": true, + "isCollaborating": false, + "isLibraryOpen": false, + "isLoading": false, + "isResizing": false, + "isRotating": false, + "lastPointerDownWith": "mouse", + "multiElement": null, + "name": "Untitled-201933152653", + "offsetLeft": 0, + "offsetTop": 0, + "openMenu": null, + "previousSelectedElementIds": Object { + "id0": true, + "id2": true, + }, + "resizingElement": null, + "scrollX": 0, + "scrollY": 0, + "scrolledOutside": false, + "selectedElementIds": Object { + "id1": true, + "id3": true, + }, + "selectedGroupIds": Object {}, + "selectionElement": null, + "shouldAddWatermark": false, + "shouldCacheIgnoreZoom": false, + "showShortcutsDialog": false, + "startBoundElement": null, + "suggestedBindings": Array [], + "username": "", + "viewBackgroundColor": "#ffffff", + "width": 1024, + "zenModeEnabled": false, + "zoom": 1, +} +`; + +exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when clicking intersection between A and B B should be selected on pointer up: [end of test] element 0 1`] = ` +Object { + "angle": 0, + "backgroundColor": "#fa5252", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 1000, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 1000, + "x": 0, + "y": 0, +} +`; + +exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when clicking intersection between A and B B should be selected on pointer up: [end of test] element 1 1`] = ` +Object { + "angle": 0, + "backgroundColor": "#fa5252", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 1000, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 453191, + "width": 1000, + "x": 500, + "y": 500, +} +`; + +exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when clicking intersection between A and B B should be selected on pointer up: [end of test] history 1`] = ` +Object { + "recording": false, + "redoStack": Array [], + "stateHistory": Array [ + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object {}, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "#fa5252", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 1000, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 1000, + "x": 0, + "y": 0, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id1": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "#fa5252", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 1000, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 1000, + "x": 0, + "y": 0, + }, + Object { + "angle": 0, + "backgroundColor": "#fa5252", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 1000, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 453191, + "width": 1000, + "x": 500, + "y": 500, + }, + ], + }, + ], +} +`; + +exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when clicking intersection between A and B B should be selected on pointer up: [end of test] number of elements 1`] = `2`; + +exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when clicking intersection between A and B B should be selected on pointer up: [end of test] number of renders 1`] = `17`; + +exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when dragging on intersection between A and B A should be dragged and keep being selected: [end of test] appState 1`] = ` +Object { + "appearance": "light", + "collaborators": Map {}, + "currentItemBackgroundColor": "#fa5252", + "currentItemFillStyle": "hachure", + "currentItemFontFamily": 1, + "currentItemFontSize": 20, + "currentItemLinearStrokeSharpness": "round", + "currentItemOpacity": 100, + "currentItemRoughness": 1, + "currentItemStrokeColor": "#000000", + "currentItemStrokeSharpness": "sharp", + "currentItemStrokeStyle": "solid", + "currentItemStrokeWidth": 1, + "currentItemTextAlign": "left", + "cursorButton": "up", + "cursorX": 0, + "cursorY": 0, + "draggingElement": null, + "editingElement": null, + "editingGroupId": null, + "editingLinearElement": null, + "elementLocked": false, + "elementType": "selection", + "errorMessage": null, + "exportBackground": true, + "gridSize": null, + "height": 768, + "isBindingEnabled": true, + "isCollaborating": false, + "isLibraryOpen": false, + "isLoading": false, + "isResizing": false, + "isRotating": false, + "lastPointerDownWith": "mouse", + "multiElement": null, + "name": "Untitled-201933152653", + "offsetLeft": 0, + "offsetTop": 0, + "openMenu": null, + "previousSelectedElementIds": Object { + "id0": true, + "id2": true, + }, + "resizingElement": null, + "scrollX": 0, + "scrollY": 0, + "scrolledOutside": false, + "selectedElementIds": Object { + "id0": true, + "id2": true, + "id3": true, + }, + "selectedGroupIds": Object {}, + "selectionElement": null, + "shouldAddWatermark": false, + "shouldCacheIgnoreZoom": false, + "showShortcutsDialog": false, + "startBoundElement": null, + "suggestedBindings": Array [], + "username": "", + "viewBackgroundColor": "#ffffff", + "width": 1024, + "zenModeEnabled": false, + "zoom": 1, +} +`; + +exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when dragging on intersection between A and B A should be dragged and keep being selected: [end of test] element 0 1`] = ` +Object { + "angle": 0, + "backgroundColor": "#fa5252", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 1000, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 3, + "versionNonce": 1150084233, + "width": 1000, + "x": 100, + "y": 100, +} +`; + +exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when dragging on intersection between A and B A should be dragged and keep being selected: [end of test] element 1 1`] = ` +Object { + "angle": 0, + "backgroundColor": "#fa5252", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 1000, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 453191, + "width": 1000, + "x": 500, + "y": 500, +} +`; + +exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when dragging on intersection between A and B A should be dragged and keep being selected: [end of test] history 1`] = ` +Object { + "recording": false, + "redoStack": Array [], + "stateHistory": Array [ + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object {}, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "#fa5252", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 1000, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 1000, + "x": 0, + "y": 0, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id1": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "#fa5252", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 1000, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 1000, + "x": 0, + "y": 0, + }, + Object { + "angle": 0, + "backgroundColor": "#fa5252", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 1000, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 453191, + "width": 1000, + "x": 500, + "y": 500, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + "id2": true, + "id3": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "#fa5252", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 1000, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 3, + "versionNonce": 1150084233, + "width": 1000, + "x": 100, + "y": 100, + }, + Object { + "angle": 0, + "backgroundColor": "#fa5252", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 1000, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 453191, + "width": 1000, + "x": 500, + "y": 500, + }, + ], + }, + ], +} +`; + +exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when dragging on intersection between A and B A should be dragged and keep being selected: [end of test] number of elements 1`] = `2`; + +exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when dragging on intersection between A and B A should be dragged and keep being selected: [end of test] number of renders 1`] = `18`; + exports[`regression tests hotkey 2 selects rectangle tool: [end of test] appState 1`] = ` Object { "appearance": "light", @@ -6261,148 +10032,6 @@ exports[`regression tests hotkey x selects draw tool: [end of test] number of el exports[`regression tests hotkey x selects draw tool: [end of test] number of renders 1`] = `7`; -exports[`regression tests keeps selected element selected when click hits element bounding box but doesn't hit the element: [end of test] appState 1`] = ` -Object { - "appearance": "light", - "collaborators": Map {}, - "currentItemBackgroundColor": "transparent", - "currentItemFillStyle": "hachure", - "currentItemFontFamily": 1, - "currentItemFontSize": 20, - "currentItemLinearStrokeSharpness": "round", - "currentItemOpacity": 100, - "currentItemRoughness": 1, - "currentItemStrokeColor": "#000000", - "currentItemStrokeSharpness": "sharp", - "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 1, - "currentItemTextAlign": "left", - "cursorButton": "up", - "cursorX": 0, - "cursorY": 0, - "draggingElement": null, - "editingElement": null, - "editingGroupId": null, - "editingLinearElement": null, - "elementLocked": false, - "elementType": "selection", - "errorMessage": null, - "exportBackground": true, - "gridSize": null, - "height": 768, - "isBindingEnabled": true, - "isCollaborating": false, - "isLibraryOpen": false, - "isLoading": false, - "isResizing": false, - "isRotating": false, - "lastPointerDownWith": "mouse", - "multiElement": null, - "name": "Untitled-201933152653", - "offsetLeft": 0, - "offsetTop": 0, - "openMenu": null, - "previousSelectedElementIds": Object { - "id0": true, - }, - "resizingElement": null, - "scrollX": 0, - "scrollY": 0, - "scrolledOutside": false, - "selectedElementIds": Object { - "id0": true, - "id1": true, - }, - "selectedGroupIds": Object {}, - "selectionElement": null, - "shouldAddWatermark": false, - "shouldCacheIgnoreZoom": false, - "showShortcutsDialog": false, - "startBoundElement": null, - "suggestedBindings": Array [], - "username": "", - "viewBackgroundColor": "#ffffff", - "width": 1024, - "zenModeEnabled": false, - "zoom": 1, -} -`; - -exports[`regression tests keeps selected element selected when click hits element bounding box but doesn't hit the element: [end of test] element 0 1`] = ` -Object { - "angle": 0, - "backgroundColor": "transparent", - "boundElementIds": null, - "fillStyle": "hachure", - "groupIds": Array [], - "height": 100, - "id": "id0", - "isDeleted": false, - "opacity": 100, - "roughness": 1, - "seed": 337897, - "strokeColor": "#000000", - "strokeSharpness": "sharp", - "strokeStyle": "solid", - "strokeWidth": 1, - "type": "ellipse", - "version": 2, - "versionNonce": 1278240551, - "width": 100, - "x": 0, - "y": 0, -} -`; - -exports[`regression tests keeps selected element selected when click hits element bounding box but doesn't hit the element: [end of test] history 1`] = ` -Object { - "recording": false, - "redoStack": Array [], - "stateHistory": Array [ - Object { - "appState": Object { - "editingGroupId": null, - "editingLinearElement": null, - "name": "Untitled-201933152653", - "selectedElementIds": Object { - "id0": true, - }, - "viewBackgroundColor": "#ffffff", - }, - "elements": Array [ - Object { - "angle": 0, - "backgroundColor": "transparent", - "boundElementIds": null, - "fillStyle": "hachure", - "groupIds": Array [], - "height": 100, - "id": "id0", - "isDeleted": false, - "opacity": 100, - "roughness": 1, - "seed": 337897, - "strokeColor": "#000000", - "strokeSharpness": "sharp", - "strokeStyle": "solid", - "strokeWidth": 1, - "type": "ellipse", - "version": 2, - "versionNonce": 1278240551, - "width": 100, - "x": 0, - "y": 0, - }, - ], - }, - ], -} -`; - -exports[`regression tests keeps selected element selected when click hits element bounding box but doesn't hit the element: [end of test] number of elements 1`] = `1`; - -exports[`regression tests keeps selected element selected when click hits element bounding box but doesn't hit the element: [end of test] number of renders 1`] = `9`; - exports[`regression tests make a group and duplicate it: [end of test] appState 1`] = ` Object { "appearance": "light", @@ -19683,6 +23312,148 @@ exports[`regression tests selecting 'Ungroup selection' in context menu ungroups exports[`regression tests selecting 'Ungroup selection' in context menu ungroups selected group: [end of test] number of renders 1`] = `15`; +exports[`regression tests shift click on selected element should deselect it on pointer up: [end of test] appState 1`] = ` +Object { + "appearance": "light", + "collaborators": Map {}, + "currentItemBackgroundColor": "transparent", + "currentItemFillStyle": "hachure", + "currentItemFontFamily": 1, + "currentItemFontSize": 20, + "currentItemLinearStrokeSharpness": "round", + "currentItemOpacity": 100, + "currentItemRoughness": 1, + "currentItemStrokeColor": "#000000", + "currentItemStrokeSharpness": "sharp", + "currentItemStrokeStyle": "solid", + "currentItemStrokeWidth": 1, + "currentItemTextAlign": "left", + "cursorButton": "up", + "cursorX": 0, + "cursorY": 0, + "draggingElement": null, + "editingElement": null, + "editingGroupId": null, + "editingLinearElement": null, + "elementLocked": false, + "elementType": "selection", + "errorMessage": null, + "exportBackground": true, + "gridSize": null, + "height": 768, + "isBindingEnabled": true, + "isCollaborating": false, + "isLibraryOpen": false, + "isLoading": false, + "isResizing": false, + "isRotating": false, + "lastPointerDownWith": "mouse", + "multiElement": null, + "name": "Untitled-201933152653", + "offsetLeft": 0, + "offsetTop": 0, + "openMenu": null, + "previousSelectedElementIds": Object { + "id0": true, + }, + "resizingElement": null, + "scrollX": 0, + "scrollY": 0, + "scrolledOutside": false, + "selectedElementIds": Object { + "id0": false, + "id1": true, + }, + "selectedGroupIds": Object {}, + "selectionElement": null, + "shouldAddWatermark": false, + "shouldCacheIgnoreZoom": false, + "showShortcutsDialog": false, + "startBoundElement": null, + "suggestedBindings": Array [], + "username": "", + "viewBackgroundColor": "#ffffff", + "width": 1024, + "zenModeEnabled": false, + "zoom": 1, +} +`; + +exports[`regression tests shift click on selected element should deselect it on pointer up: [end of test] element 0 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, +} +`; + +exports[`regression tests shift click on selected element should deselect it on pointer up: [end of test] history 1`] = ` +Object { + "recording": false, + "redoStack": Array [], + "stateHistory": Array [ + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, + }, + ], + }, + ], +} +`; + +exports[`regression tests shift click on selected element should deselect it on pointer up: [end of test] number of elements 1`] = `1`; + +exports[`regression tests shift click on selected element should deselect it on pointer up: [end of test] number of renders 1`] = `9`; + exports[`regression tests shift-click to multiselect, then drag: [end of test] appState 1`] = ` Object { "appearance": "light", @@ -21503,6 +25274,656 @@ exports[`regression tests supports nested groups: [end of test] number of elemen exports[`regression tests supports nested groups: [end of test] number of renders 1`] = `31`; +exports[`regression tests switches from group of selected elements to another element on pointer down: [end of test] appState 1`] = ` +Object { + "appearance": "light", + "collaborators": Map {}, + "currentItemBackgroundColor": "transparent", + "currentItemFillStyle": "hachure", + "currentItemFontFamily": 1, + "currentItemFontSize": 20, + "currentItemLinearStrokeSharpness": "round", + "currentItemOpacity": 100, + "currentItemRoughness": 1, + "currentItemStrokeColor": "#000000", + "currentItemStrokeSharpness": "sharp", + "currentItemStrokeStyle": "solid", + "currentItemStrokeWidth": 1, + "currentItemTextAlign": "left", + "cursorButton": "down", + "cursorX": 0, + "cursorY": 0, + "draggingElement": Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 0, + "id": "id4", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 1116226695, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "selection", + "version": 1, + "versionNonce": 0, + "width": 0, + "x": 0, + "y": 0, + }, + "editingElement": null, + "editingGroupId": null, + "editingLinearElement": null, + "elementLocked": false, + "elementType": "selection", + "errorMessage": null, + "exportBackground": true, + "gridSize": null, + "height": 768, + "isBindingEnabled": true, + "isCollaborating": false, + "isLibraryOpen": false, + "isLoading": false, + "isResizing": false, + "isRotating": false, + "lastPointerDownWith": "mouse", + "multiElement": null, + "name": "Untitled-201933152653", + "offsetLeft": 0, + "offsetTop": 0, + "openMenu": null, + "previousSelectedElementIds": Object { + "id1": true, + "id2": true, + "id3": true, + }, + "resizingElement": null, + "scrollX": 0, + "scrollY": 0, + "scrolledOutside": false, + "selectedElementIds": Object { + "id0": true, + }, + "selectedGroupIds": Object {}, + "selectionElement": Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 0, + "id": "id4", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 1116226695, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "selection", + "version": 1, + "versionNonce": 0, + "width": 0, + "x": 0, + "y": 0, + }, + "shouldAddWatermark": false, + "shouldCacheIgnoreZoom": false, + "showShortcutsDialog": false, + "startBoundElement": null, + "suggestedBindings": Array [], + "username": "", + "viewBackgroundColor": "#ffffff", + "width": 1024, + "zenModeEnabled": false, + "zoom": 1, +} +`; + +exports[`regression tests switches from group of selected elements to another element on pointer down: [end of test] element 0 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, +} +`; + +exports[`regression tests switches from group of selected elements to another element on pointer down: [end of test] element 1 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 453191, + "width": 100, + "x": 110, + "y": 110, +} +`; + +exports[`regression tests switches from group of selected elements to another element on pointer down: [end of test] element 2 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id2", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 401146281, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "diamond", + "version": 2, + "versionNonce": 2019559783, + "width": 100, + "x": 310, + "y": 310, +} +`; + +exports[`regression tests switches from group of selected elements to another element on pointer down: [end of test] history 1`] = ` +Object { + "recording": false, + "redoStack": Array [], + "stateHistory": Array [ + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id1": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 453191, + "width": 100, + "x": 110, + "y": 110, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id2": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 453191, + "width": 100, + "x": 110, + "y": 110, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 100, + "id": "id2", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 401146281, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "diamond", + "version": 2, + "versionNonce": 2019559783, + "width": 100, + "x": 310, + "y": 310, + }, + ], + }, + ], +} +`; + +exports[`regression tests switches from group of selected elements to another element on pointer down: [end of test] number of elements 1`] = `3`; + +exports[`regression tests switches from group of selected elements to another element on pointer down: [end of test] number of renders 1`] = `18`; + +exports[`regression tests switches selected element on pointer down: [end of test] appState 1`] = ` +Object { + "appearance": "light", + "collaborators": Map {}, + "currentItemBackgroundColor": "transparent", + "currentItemFillStyle": "hachure", + "currentItemFontFamily": 1, + "currentItemFontSize": 20, + "currentItemLinearStrokeSharpness": "round", + "currentItemOpacity": 100, + "currentItemRoughness": 1, + "currentItemStrokeColor": "#000000", + "currentItemStrokeSharpness": "sharp", + "currentItemStrokeStyle": "solid", + "currentItemStrokeWidth": 1, + "currentItemTextAlign": "left", + "cursorButton": "down", + "cursorX": 0, + "cursorY": 0, + "draggingElement": Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 0, + "id": "id2", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 401146281, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "selection", + "version": 1, + "versionNonce": 0, + "width": 0, + "x": 0, + "y": 0, + }, + "editingElement": null, + "editingGroupId": null, + "editingLinearElement": null, + "elementLocked": false, + "elementType": "selection", + "errorMessage": null, + "exportBackground": true, + "gridSize": null, + "height": 768, + "isBindingEnabled": true, + "isCollaborating": false, + "isLibraryOpen": false, + "isLoading": false, + "isResizing": false, + "isRotating": false, + "lastPointerDownWith": "mouse", + "multiElement": null, + "name": "Untitled-201933152653", + "offsetLeft": 0, + "offsetTop": 0, + "openMenu": null, + "previousSelectedElementIds": Object { + "id1": true, + }, + "resizingElement": null, + "scrollX": 0, + "scrollY": 0, + "scrolledOutside": false, + "selectedElementIds": Object { + "id0": true, + }, + "selectedGroupIds": Object {}, + "selectionElement": Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 0, + "id": "id2", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 401146281, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "selection", + "version": 1, + "versionNonce": 0, + "width": 0, + "x": 0, + "y": 0, + }, + "shouldAddWatermark": false, + "shouldCacheIgnoreZoom": false, + "showShortcutsDialog": false, + "startBoundElement": null, + "suggestedBindings": Array [], + "username": "", + "viewBackgroundColor": "#ffffff", + "width": 1024, + "zenModeEnabled": false, + "zoom": 1, +} +`; + +exports[`regression tests switches selected element on pointer down: [end of test] element 0 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, +} +`; + +exports[`regression tests switches selected element on pointer down: [end of test] element 1 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 453191, + "width": 10, + "x": 20, + "y": 20, +} +`; + +exports[`regression tests switches selected element on pointer down: [end of test] history 1`] = ` +Object { + "recording": false, + "redoStack": Array [], + "stateHistory": Array [ + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id1": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1278240551, + "width": 10, + "x": 0, + "y": 0, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "ellipse", + "version": 2, + "versionNonce": 453191, + "width": 10, + "x": 20, + "y": 20, + }, + ], + }, + ], +} +`; + +exports[`regression tests switches selected element on pointer down: [end of test] number of elements 1`] = `2`; + +exports[`regression tests switches selected element on pointer down: [end of test] number of renders 1`] = `12`; + exports[`regression tests two-finger scroll works: [end of test] appState 1`] = ` Object { "appearance": "light", diff --git a/src/tests/regressionTests.test.tsx b/src/tests/regressionTests.test.tsx index ec880d02..04f19257 100644 --- a/src/tests/regressionTests.test.tsx +++ b/src/tests/regressionTests.test.tsx @@ -1213,13 +1213,478 @@ describe("regression tests", () => { expect(h.elements[1].groupIds).toHaveLength(0); }); - it("keeps selected element selected when click hits element bounding box but doesn't hit the element", () => { + it("deselects selected element, on pointer up, when click hits element bounding box but doesn't hit the element", () => { clickTool("ellipse"); - mouse.down(0, 0); + mouse.down(); mouse.up(100, 100); - // click on bounding box but not on element - mouse.click(0, 0); + // hits bounding box without hitting element + mouse.down(); expect(getSelectedElements().length).toBe(1); + mouse.up(); + expect(getSelectedElements().length).toBe(0); + }); + + it("switches selected element on pointer down", () => { + clickTool("rectangle"); + mouse.down(); + mouse.up(10, 10); + + clickTool("ellipse"); + mouse.down(10, 10); + mouse.up(10, 10); + + expect(getSelectedElement().type).toBe("ellipse"); + + // pointer down on rectangle + mouse.reset(); + mouse.down(); + + expect(getSelectedElement().type).toBe("rectangle"); + }); + + it("can drag element that covers another element, while another elem is selected", () => { + clickTool("rectangle"); + mouse.down(100, 100); + mouse.up(200, 200); + + clickTool("rectangle"); + mouse.reset(); + mouse.down(100, 100); + mouse.up(200, 200); + + clickTool("ellipse"); + mouse.reset(); + mouse.down(300, 300); + mouse.up(350, 350); + + expect(getSelectedElement().type).toBe("ellipse"); + + // pointer down on rectangle + mouse.reset(); + mouse.down(100, 100); + mouse.up(200, 200); + + expect(getSelectedElement().type).toBe("rectangle"); + }); + + it("deselects selected element on pointer down when pointer doesn't hit any element", () => { + clickTool("rectangle"); + mouse.down(); + mouse.up(10, 10); + + expect(getSelectedElements().length).toBe(1); + + // pointer down on space without elements + mouse.down(100, 100); + + expect(getSelectedElements().length).toBe(0); + }); + + it("Drags selected element when hitting only bounding box and keeps element selected", () => { + clickTool("ellipse"); + mouse.down(); + mouse.up(10, 10); + + const { x: prevX, y: prevY } = getSelectedElement(); + + // drag element from point on bounding box that doesn't hit element + mouse.reset(); + mouse.down(); + mouse.up(25, 25); + + expect(getSelectedElement().x).toEqual(prevX + 25); + expect(getSelectedElement().y).toEqual(prevY + 25); + }); + + it( + "given selected element A with lower z-index than unselected element B and given B is partially over A " + + "when clicking intersection between A and B " + + "B should be selected on pointer up", + () => { + clickTool("rectangle"); + // change background color since default is transparent + // and transparent elements can't be selected by clicking inside of them + clickLabeledElement("Background"); + clickLabeledElement("#fa5252"); + mouse.down(); + mouse.up(1000, 1000); + + // draw ellipse partially over rectangle. + // since ellipse was created after rectangle it has an higher z-index. + // we don't need to change background color again since change above + // affects next drawn elements. + clickTool("ellipse"); + mouse.reset(); + mouse.down(500, 500); + mouse.up(1000, 1000); + + // select rectangle + mouse.reset(); + mouse.click(); + + // pointer down on intersection between ellipse and rectangle + mouse.down(900, 900); + expect(getSelectedElement().type).toBe("rectangle"); + + mouse.up(); + expect(getSelectedElement().type).toBe("ellipse"); + }, + ); + + it( + "given selected element A with lower z-index than unselected element B and given B is partially over A " + + "when dragging on intersection between A and B " + + "A should be dragged and keep being selected", + () => { + clickTool("rectangle"); + // change background color since default is transparent + // and transparent elements can't be selected by clicking inside of them + clickLabeledElement("Background"); + clickLabeledElement("#fa5252"); + mouse.down(); + mouse.up(1000, 1000); + + // draw ellipse partially over rectangle. + // since ellipse was created after rectangle it has an higher z-index. + // we don't need to change background color again since change above + // affects next drawn elements. + clickTool("ellipse"); + mouse.reset(); + mouse.down(500, 500); + mouse.up(1000, 1000); + + // select rectangle + mouse.reset(); + mouse.click(); + + const { x: prevX, y: prevY } = getSelectedElement(); + + // pointer down on intersection between ellipse and rectangle + mouse.down(900, 900); + mouse.up(100, 100); + + expect(getSelectedElement().type).toBe("rectangle"); + expect(getSelectedElement().x).toEqual(prevX + 100); + expect(getSelectedElement().y).toEqual(prevY + 100); + }, + ); + + it("deselects group of selected elements on pointer down when pointer doesn't hit any element", () => { + clickTool("rectangle"); + mouse.down(); + mouse.up(10, 10); + + clickTool("ellipse"); + mouse.down(100, 100); + mouse.up(10, 10); + + // Selects first element without deselecting the second element + // Second element is already selected because creating it was our last action + mouse.reset(); + withModifierKeys({ shift: true }, () => { + mouse.click(5, 5); + }); + + expect(getSelectedElements().length).toBe(2); + + // pointer down on space without elements + mouse.reset(); + mouse.down(500, 500); + + expect(getSelectedElements().length).toBe(0); + }); + + it("switches from group of selected elements to another element on pointer down", () => { + clickTool("rectangle"); + mouse.down(); + mouse.up(10, 10); + + clickTool("ellipse"); + mouse.down(100, 100); + mouse.up(100, 100); + + clickTool("diamond"); + mouse.down(100, 100); + mouse.up(100, 100); + + // Selects ellipse without deselecting the diamond + // Diamond is already selected because creating it was our last action + mouse.reset(); + withModifierKeys({ shift: true }, () => { + mouse.click(110, 160); + }); + + expect(getSelectedElements().length).toBe(2); + + // select rectangle + mouse.reset(); + mouse.down(); + + expect(getSelectedElement().type).toBe("rectangle"); + }); + + it("deselects group of selected elements on pointer up when pointer hits common bounding box without hitting any element", () => { + clickTool("rectangle"); + mouse.down(); + mouse.up(10, 10); + + clickTool("ellipse"); + mouse.down(100, 100); + mouse.up(10, 10); + + // Selects first element without deselecting the second element + // Second element is already selected because creating it was our last action + mouse.reset(); + withModifierKeys({ shift: true }, () => { + mouse.click(5, 5); + }); + + // pointer down on common bounding box without hitting any of the elements + mouse.reset(); + mouse.down(50, 50); + expect(getSelectedElements().length).toBe(2); + + mouse.up(); + expect(getSelectedElements().length).toBe(0); + }); + + it( + "drags selected elements from point inside common bounding box that doesn't hit any element " + + "and keeps elements selected after dragging", + () => { + clickTool("rectangle"); + mouse.down(); + mouse.up(10, 10); + + clickTool("ellipse"); + mouse.down(100, 100); + mouse.up(10, 10); + + // Selects first element without deselecting the second element + // Second element is already selected because creating it was our last action + mouse.reset(); + withModifierKeys({ shift: true }, () => { + mouse.click(5, 5); + }); + + expect(getSelectedElements().length).toBe(2); + + const { + x: firstElementPrevX, + y: firstElementPrevY, + } = getSelectedElements()[0]; + const { + x: secondElementPrevX, + y: secondElementPrevY, + } = getSelectedElements()[1]; + + // drag elements from point on common bounding box that doesn't hit any of the elements + mouse.reset(); + mouse.down(50, 50); + mouse.up(25, 25); + + expect(getSelectedElements()[0].x).toEqual(firstElementPrevX + 25); + expect(getSelectedElements()[0].y).toEqual(firstElementPrevY + 25); + + expect(getSelectedElements()[1].x).toEqual(secondElementPrevX + 25); + expect(getSelectedElements()[1].y).toEqual(secondElementPrevY + 25); + + expect(getSelectedElements().length).toBe(2); + }, + ); + + it( + "given a group of selected elements with an element that is not selected inside the group common bounding box " + + "when element that is not selected is clicked " + + "should switch selection to not selected element on pointer up", + () => { + clickTool("rectangle"); + mouse.down(); + mouse.up(10, 10); + + clickTool("ellipse"); + mouse.down(100, 100); + mouse.up(100, 100); + + clickTool("diamond"); + mouse.down(100, 100); + mouse.up(100, 100); + + // Selects rectangle without deselecting the diamond + // Diamond is already selected because creating it was our last action + mouse.reset(); + withModifierKeys({ shift: true }, () => { + mouse.click(); + }); + + // pointer down on ellipse + mouse.down(110, 160); + expect(getSelectedElements().length).toBe(2); + + mouse.up(); + expect(getSelectedElement().type).toBe("ellipse"); + }, + ); + + it( + "given a selected element A and a not selected element B with higher z-index than A " + + "and given B partialy overlaps A " + + "when there's a shift-click on the overlapped section B is added to the selection", + () => { + clickTool("rectangle"); + // change background color since default is transparent + // and transparent elements can't be selected by clicking inside of them + clickLabeledElement("Background"); + clickLabeledElement("#fa5252"); + mouse.down(); + mouse.up(1000, 1000); + + // draw ellipse partially over rectangle. + // since ellipse was created after rectangle it has an higher z-index. + // we don't need to change background color again since change above + // affects next drawn elements. + clickTool("ellipse"); + mouse.reset(); + mouse.down(500, 500); + mouse.up(1000, 1000); + + // select rectangle + mouse.reset(); + mouse.click(); + + // click on intersection between ellipse and rectangle + withModifierKeys({ shift: true }, () => { + mouse.click(900, 900); + }); + + expect(getSelectedElements().length).toBe(2); + }, + ); + + it("shift click on selected element should deselect it on pointer up", () => { + clickTool("rectangle"); + mouse.down(); + mouse.up(10, 10); + + // Rectangle is already selected since creating + // it was our last action + withModifierKeys({ shift: true }, () => { + mouse.down(); + }); + expect(getSelectedElements().length).toBe(1); + + withModifierKeys({ shift: true }, () => { + mouse.up(); + }); + expect(getSelectedElements().length).toBe(0); }); }); + +it( + "given element A and group of elements B and given both are selected " + + "when user clicks on B, on pointer up " + + "only elements from B should be selected", + () => { + clickTool("rectangle"); + mouse.down(); + mouse.up(100, 100); + + clickTool("rectangle"); + mouse.down(10, 10); + mouse.up(100, 100); + + clickTool("rectangle"); + mouse.down(10, 10); + mouse.up(100, 100); + + // Select first rectangle while keeping third one selected. + // Third rectangle is selected because it was the last element + // to be created. + mouse.reset(); + withModifierKeys({ shift: true }, () => { + mouse.click(); + }); + + // Create group with first and third rectangle + withModifierKeys({ ctrl: true }, () => { + keyPress("g"); + }); + + expect(getSelectedElements().length).toBe(2); + const selectedGroupIds = Object.keys(h.state.selectedGroupIds); + expect(selectedGroupIds.length).toBe(1); + + // Select second rectangle without deselecting group + withModifierKeys({ shift: true }, () => { + mouse.click(110, 110); + }); + expect(getSelectedElements().length).toBe(3); + + // pointer down on first rectangle that is + // part of the group + mouse.reset(); + mouse.down(); + expect(getSelectedElements().length).toBe(3); + + // should only deselect on pointer up + mouse.up(); + expect(getSelectedElements().length).toBe(2); + const newSelectedGroupIds = Object.keys(h.state.selectedGroupIds); + expect(newSelectedGroupIds.length).toBe(1); + }, +); + +it( + "given element A and group of elements B and given both are selected " + + "when user shift-clicks on B, on pointer up " + + "only element A should be selected", + () => { + clickTool("rectangle"); + mouse.down(); + mouse.up(100, 100); + + clickTool("rectangle"); + mouse.down(10, 10); + mouse.up(100, 100); + + clickTool("rectangle"); + mouse.down(10, 10); + mouse.up(100, 100); + + // Select first rectangle while keeping third one selected. + // Third rectangle is selected because it was the last element + // to be created. + mouse.reset(); + withModifierKeys({ shift: true }, () => { + mouse.click(); + }); + + // Create group with first and third rectangle + withModifierKeys({ ctrl: true }, () => { + keyPress("g"); + }); + + expect(getSelectedElements().length).toBe(2); + const selectedGroupIds = Object.keys(h.state.selectedGroupIds); + expect(selectedGroupIds.length).toBe(1); + + // Select second rectangle without deselecting group + withModifierKeys({ shift: true }, () => { + mouse.click(110, 110); + }); + expect(getSelectedElements().length).toBe(3); + + // pointer down o first rectangle that is + // part of the group + mouse.reset(); + withModifierKeys({ shift: true }, () => { + mouse.down(); + }); + expect(getSelectedElements().length).toBe(3); + withModifierKeys({ shift: true }, () => { + mouse.up(); + }); + expect(getSelectedElements().length).toBe(1); + }, +);