From b8f8bc2e32207d08497d5b7283b942b79d336aa1 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Thu, 27 Aug 2020 20:32:10 +0200 Subject: [PATCH] fix group selection (#2092) --- src/components/App.tsx | 34 +- .../regressionTests.test.tsx.snap | 898 +++++++++++++++++- src/tests/regressionTests.test.tsx | 116 ++- 3 files changed, 936 insertions(+), 112 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 1ff1005f..a5a48bfb 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -3384,33 +3384,15 @@ class App extends React.Component { })); } } else { - 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 }), {}), + this.setState((prevState) => ({ + ...selectGroupsForSelectedElements( + { + ...prevState, + selectedElementIds: { [hitElement.id]: true }, }, - selectedElementIds: { ...idsOfSelectedElementsThatAreInGroups }, - })); - } else { - this.setState((_prevState) => ({ - selectedGroupIds: {}, - selectedElementIds: { [hitElement!.id]: true }, - })); - } + this.scene.getElements(), + ), + })); } } diff --git a/src/tests/__snapshots__/regressionTests.test.tsx.snap b/src/tests/__snapshots__/regressionTests.test.tsx.snap index 7e2e239e..a6f2a618 100644 --- a/src/tests/__snapshots__/regressionTests.test.tsx.snap +++ b/src/tests/__snapshots__/regressionTests.test.tsx.snap @@ -46,7 +46,8 @@ Object { "id1": true, "id2": true, "id3": true, - "id5": true, + "id4": true, + "id6": true, }, "resizingElement": null, "scrollX": 0, @@ -55,10 +56,10 @@ Object { "selectedElementIds": Object { "id0": true, "id2": true, - "id6": true, + "id7": true, }, "selectedGroupIds": Object { - "id4": true, + "id5": true, }, "selectionElement": null, "shouldAddWatermark": false, @@ -81,7 +82,7 @@ Object { "boundElementIds": null, "fillStyle": "hachure", "groupIds": Array [], - "height": 100, + "height": 10, "id": "id1", "isDeleted": false, "opacity": 100, @@ -94,9 +95,9 @@ Object { "type": "rectangle", "version": 2, "versionNonce": 453191, - "width": 100, - "x": 110, - "y": 110, + "width": 10, + "x": 0, + "y": 30, } `; @@ -107,9 +108,9 @@ Object { "boundElementIds": null, "fillStyle": "hachure", "groupIds": Array [ - "id4", + "id5", ], - "height": 100, + "height": 10, "id": "id0", "isDeleted": false, "opacity": 100, @@ -121,8 +122,8 @@ Object { "strokeWidth": 1, "type": "rectangle", "version": 3, - "versionNonce": 1116226695, - "width": 100, + "versionNonce": 1014066025, + "width": 10, "x": 0, "y": 0, } @@ -135,9 +136,9 @@ Object { "boundElementIds": null, "fillStyle": "hachure", "groupIds": Array [ - "id4", + "id5", ], - "height": 100, + "height": 10, "id": "id2", "isDeleted": false, "opacity": 100, @@ -149,10 +150,10 @@ Object { "strokeWidth": 1, "type": "rectangle", "version": 3, - "versionNonce": 1014066025, - "width": 100, - "x": 220, - "y": 220, + "versionNonce": 238820263, + "width": 10, + "x": 0, + "y": 60, } `; @@ -178,7 +179,7 @@ Object { "boundElementIds": null, "fillStyle": "hachure", "groupIds": Array [], - "height": 100, + "height": 10, "id": "id0", "isDeleted": false, "opacity": 100, @@ -191,7 +192,7 @@ Object { "type": "rectangle", "version": 2, "versionNonce": 1278240551, - "width": 100, + "width": 10, "x": 0, "y": 0, }, @@ -214,7 +215,7 @@ Object { "boundElementIds": null, "fillStyle": "hachure", "groupIds": Array [], - "height": 100, + "height": 10, "id": "id0", "isDeleted": false, "opacity": 100, @@ -227,7 +228,7 @@ Object { "type": "rectangle", "version": 2, "versionNonce": 1278240551, - "width": 100, + "width": 10, "x": 0, "y": 0, }, @@ -237,7 +238,7 @@ Object { "boundElementIds": null, "fillStyle": "hachure", "groupIds": Array [], - "height": 100, + "height": 10, "id": "id1", "isDeleted": false, "opacity": 100, @@ -250,9 +251,9 @@ Object { "type": "rectangle", "version": 2, "versionNonce": 453191, - "width": 100, - "x": 110, - "y": 110, + "width": 10, + "x": 0, + "y": 30, }, ], }, @@ -273,7 +274,7 @@ Object { "boundElementIds": null, "fillStyle": "hachure", "groupIds": Array [], - "height": 100, + "height": 10, "id": "id0", "isDeleted": false, "opacity": 100, @@ -286,7 +287,7 @@ Object { "type": "rectangle", "version": 2, "versionNonce": 1278240551, - "width": 100, + "width": 10, "x": 0, "y": 0, }, @@ -296,7 +297,7 @@ Object { "boundElementIds": null, "fillStyle": "hachure", "groupIds": Array [], - "height": 100, + "height": 10, "id": "id1", "isDeleted": false, "opacity": 100, @@ -309,9 +310,9 @@ Object { "type": "rectangle", "version": 2, "versionNonce": 453191, - "width": 100, - "x": 110, - "y": 110, + "width": 10, + "x": 0, + "y": 30, }, Object { "angle": 0, @@ -319,7 +320,7 @@ Object { "boundElementIds": null, "fillStyle": "hachure", "groupIds": Array [], - "height": 100, + "height": 10, "id": "id2", "isDeleted": false, "opacity": 100, @@ -332,9 +333,9 @@ Object { "type": "rectangle", "version": 2, "versionNonce": 2019559783, - "width": 100, - "x": 220, - "y": 220, + "width": 10, + "x": 0, + "y": 60, }, ], }, @@ -347,6 +348,7 @@ Object { "id0": true, "id2": true, "id3": true, + "id4": true, }, "viewBackgroundColor": "#ffffff", }, @@ -357,7 +359,7 @@ Object { "boundElementIds": null, "fillStyle": "hachure", "groupIds": Array [], - "height": 100, + "height": 10, "id": "id1", "isDeleted": false, "opacity": 100, @@ -370,9 +372,9 @@ Object { "type": "rectangle", "version": 2, "versionNonce": 453191, - "width": 100, - "x": 110, - "y": 110, + "width": 10, + "x": 0, + "y": 30, }, Object { "angle": 0, @@ -380,9 +382,9 @@ Object { "boundElementIds": null, "fillStyle": "hachure", "groupIds": Array [ - "id4", + "id5", ], - "height": 100, + "height": 10, "id": "id0", "isDeleted": false, "opacity": 100, @@ -394,8 +396,8 @@ Object { "strokeWidth": 1, "type": "rectangle", "version": 3, - "versionNonce": 1116226695, - "width": 100, + "versionNonce": 1014066025, + "width": 10, "x": 0, "y": 0, }, @@ -405,9 +407,9 @@ Object { "boundElementIds": null, "fillStyle": "hachure", "groupIds": Array [ - "id4", + "id5", ], - "height": 100, + "height": 10, "id": "id2", "isDeleted": false, "opacity": 100, @@ -419,10 +421,10 @@ Object { "strokeWidth": 1, "type": "rectangle", "version": 3, - "versionNonce": 1014066025, - "width": 100, - "x": 220, - "y": 220, + "versionNonce": 238820263, + "width": 10, + "x": 0, + "y": 60, }, ], }, @@ -432,7 +434,7 @@ Object { 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 clicks on B, on pointer up only elements from B should be selected: [end of test] number of renders 1`] = `26`; 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 { @@ -24495,6 +24497,802 @@ exports[`regression tests shows context menu for element: [end of test] number o exports[`regression tests shows context menu for element: [end of test] number of renders 1`] = `7`; +exports[`regression tests single-clicking on a subgroup of a selected group should not alter selection: [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, + "id5": true, + "id6": true, + }, + "resizingElement": null, + "scrollX": 0, + "scrollY": 0, + "scrolledOutside": false, + "selectedElementIds": Object { + "id0": true, + "id1": true, + "id11": true, + "id5": true, + "id6": true, + }, + "selectedGroupIds": Object { + "id10": true, + }, + "selectionElement": null, + "shouldAddWatermark": false, + "shouldCacheIgnoreZoom": false, + "showShortcutsDialog": false, + "startBoundElement": null, + "suggestedBindings": Array [], + "username": "", + "viewBackgroundColor": "#ffffff", + "width": 1024, + "zenModeEnabled": false, + "zoom": 1, +} +`; + +exports[`regression tests single-clicking on a subgroup of a selected group should not alter selection: [end of test] element 0 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id4", + "id10", + ], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 4, + "versionNonce": 81784553, + "width": 10, + "x": 10, + "y": 10, +} +`; + +exports[`regression tests single-clicking on a subgroup of a selected group should not alter selection: [end of test] element 1 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id4", + "id10", + ], + "height": 10, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 4, + "versionNonce": 747212839, + "width": 10, + "x": 50, + "y": 50, +} +`; + +exports[`regression tests single-clicking on a subgroup of a selected group should not alter selection: [end of test] element 2 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id9", + "id10", + ], + "height": 10, + "id": "id5", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 1014066025, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 4, + "versionNonce": 1723083209, + "width": 10, + "x": 10, + "y": 50, +} +`; + +exports[`regression tests single-clicking on a subgroup of a selected group should not alter selection: [end of test] element 3 1`] = ` +Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id9", + "id10", + ], + "height": 10, + "id": "id6", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 400692809, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 4, + "versionNonce": 760410951, + "width": 10, + "x": 50, + "y": 50, +} +`; + +exports[`regression tests single-clicking on a subgroup of a selected group should not alter 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 { + "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": 10, + "y": 10, + }, + ], + }, + 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": 10, + "y": 10, + }, + 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": "rectangle", + "version": 2, + "versionNonce": 453191, + "width": 10, + "x": 50, + "y": 50, + }, + ], + }, + 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 [ + "id4", + ], + "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": 10, + "y": 10, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id4", + ], + "height": 10, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 3, + "versionNonce": 1116226695, + "width": 10, + "x": 50, + "y": 50, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id5": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id4", + ], + "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": 10, + "y": 10, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id4", + ], + "height": 10, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 3, + "versionNonce": 1116226695, + "width": 10, + "x": 50, + "y": 50, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id5", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 1014066025, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 238820263, + "width": 10, + "x": 10, + "y": 50, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id6": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id4", + ], + "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": 10, + "y": 10, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id4", + ], + "height": 10, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 3, + "versionNonce": 1116226695, + "width": 10, + "x": 50, + "y": 50, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id5", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 1014066025, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 238820263, + "width": 10, + "x": 10, + "y": 50, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [], + "height": 10, + "id": "id6", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 400692809, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 2, + "versionNonce": 1604849351, + "width": 10, + "x": 50, + "y": 50, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id5": true, + "id6": true, + "id7": true, + "id8": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id4", + ], + "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": 10, + "y": 10, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id4", + ], + "height": 10, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 3, + "versionNonce": 1116226695, + "width": 10, + "x": 50, + "y": 50, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id9", + ], + "height": 10, + "id": "id5", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 1014066025, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 3, + "versionNonce": 493213705, + "width": 10, + "x": 10, + "y": 50, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id9", + ], + "height": 10, + "id": "id6", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 400692809, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 3, + "versionNonce": 915032327, + "width": 10, + "x": 50, + "y": 50, + }, + ], + }, + Object { + "appState": Object { + "editingGroupId": null, + "editingLinearElement": null, + "name": "Untitled-201933152653", + "selectedElementIds": Object { + "id0": true, + "id1": true, + "id5": true, + "id6": true, + }, + "viewBackgroundColor": "#ffffff", + }, + "elements": Array [ + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id4", + "id10", + ], + "height": 10, + "id": "id0", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 337897, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 4, + "versionNonce": 81784553, + "width": 10, + "x": 10, + "y": 10, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id4", + "id10", + ], + "height": 10, + "id": "id1", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 449462985, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 4, + "versionNonce": 747212839, + "width": 10, + "x": 50, + "y": 50, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id9", + "id10", + ], + "height": 10, + "id": "id5", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 1014066025, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 4, + "versionNonce": 1723083209, + "width": 10, + "x": 10, + "y": 50, + }, + Object { + "angle": 0, + "backgroundColor": "transparent", + "boundElementIds": null, + "fillStyle": "hachure", + "groupIds": Array [ + "id9", + "id10", + ], + "height": 10, + "id": "id6", + "isDeleted": false, + "opacity": 100, + "roughness": 1, + "seed": 400692809, + "strokeColor": "#000000", + "strokeSharpness": "sharp", + "strokeStyle": "solid", + "strokeWidth": 1, + "type": "rectangle", + "version": 4, + "versionNonce": 760410951, + "width": 10, + "x": 50, + "y": 50, + }, + ], + }, + ], +} +`; + +exports[`regression tests single-clicking on a subgroup of a selected group should not alter selection: [end of test] number of elements 1`] = `4`; + +exports[`regression tests single-clicking on a subgroup of a selected group should not alter selection: [end of test] number of renders 1`] = `37`; + exports[`regression tests spacebar + drag scrolls the canvas: [end of test] appState 1`] = ` Object { "appearance": "light", diff --git a/src/tests/regressionTests.test.tsx b/src/tests/regressionTests.test.tsx index 04f19257..a1cd9806 100644 --- a/src/tests/regressionTests.test.tsx +++ b/src/tests/regressionTests.test.tsx @@ -23,6 +23,33 @@ const clickTool = (toolName: ToolName) => { fireEvent.click(getByToolName(toolName)); }; +const createElement = ( + type: ToolName, + { + x = 0, + y = x, + size = 10, + }: { + x?: number; + y?: number; + size?: number; + }, +) => { + clickTool(type); + mouse.reset(); + mouse.down(x, y); + mouse.reset(); + mouse.up(x + size, y + size); + return h.elements[h.elements.length - 1]; +}; + +const group = (elements: ExcalidrawElement[]) => { + mouse.select(elements); + withModifierKeys({ ctrl: true }, () => { + keyPress("g"); + }); +}; + let altKey = false; let shiftKey = false; let ctrlKey = false; @@ -159,6 +186,28 @@ class Pointer { this.move(dx, dy); fireEvent.doubleClick(canvas, this.getEvent()); } + + select( + /** if multiple elements supplied, they're shift-selected */ + elements: ExcalidrawElement | ExcalidrawElement[], + ) { + // @ts-ignore + h.app.clearSelection(null); + withModifierKeys({ shift: true }, () => { + elements = Array.isArray(elements) ? elements : [elements]; + elements.forEach((element) => { + mouse.reset(); + mouse.click(element.x, element.y); + }); + }); + mouse.reset(); + } + + clickOn(element: ExcalidrawElement) { + mouse.reset(); + mouse.click(element.x, element.y); + mouse.reset(); + } } const mouse = new Pointer("mouse"); @@ -1579,6 +1628,27 @@ describe("regression tests", () => { }); expect(getSelectedElements().length).toBe(0); }); + + it("single-clicking on a subgroup of a selected group should not alter selection", () => { + const rect1 = createElement("rectangle", { x: 10 }); + const rect2 = createElement("rectangle", { x: 50 }); + group([rect1, rect2]); + + const rect3 = createElement("rectangle", { x: 10, y: 50 }); + const rect4 = createElement("rectangle", { x: 50, y: 50 }); + group([rect3, rect4]); + + withModifierKeys({ ctrl: true }, () => { + keyPress("a"); + keyPress("g"); + }); + + const selectedGroupIds_prev = h.state.selectedGroupIds; + const selectedElements_prev = getSelectedElements(); + mouse.clickOn(rect3); + expect(h.state.selectedGroupIds).toEqual(selectedGroupIds_prev); + expect(getSelectedElements()).toEqual(selectedElements_prev); + }); }); it( @@ -1586,52 +1656,26 @@ it( "when user clicks on B, on pointer up " + "only elements from B should be selected", () => { - clickTool("rectangle"); - mouse.down(); - mouse.up(100, 100); + const rect1 = createElement("rectangle", { y: 0 }); + const rect2 = createElement("rectangle", { y: 30 }); + const rect3 = createElement("rectangle", { y: 60 }); - 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"); - }); + group([rect1, rect3]); expect(getSelectedElements().length).toBe(2); - const selectedGroupIds = Object.keys(h.state.selectedGroupIds); - expect(selectedGroupIds.length).toBe(1); + expect(Object.keys(h.state.selectedGroupIds).length).toBe(1); // Select second rectangle without deselecting group withModifierKeys({ shift: true }, () => { - mouse.click(110, 110); + mouse.clickOn(rect2); }); 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(); + // clicking on first rectangle that is part of the group should select + // that group (exclusively) + mouse.clickOn(rect1); expect(getSelectedElements().length).toBe(2); - const newSelectedGroupIds = Object.keys(h.state.selectedGroupIds); - expect(newSelectedGroupIds.length).toBe(1); + expect(Object.keys(h.state.selectedGroupIds).length).toBe(1); }, );