align.test.tsx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. import { KEYS } from "@excalidraw/common";
  2. import {
  3. actionAlignVerticallyCentered,
  4. actionAlignHorizontallyCentered,
  5. actionGroup,
  6. actionAlignTop,
  7. actionAlignBottom,
  8. actionAlignLeft,
  9. actionAlignRight,
  10. } from "@excalidraw/excalidraw/actions";
  11. import { defaultLang, setLanguage } from "@excalidraw/excalidraw/i18n";
  12. import { Excalidraw } from "@excalidraw/excalidraw";
  13. import { API } from "@excalidraw/excalidraw/tests/helpers/api";
  14. import { UI, Pointer, Keyboard } from "@excalidraw/excalidraw/tests/helpers/ui";
  15. import {
  16. act,
  17. unmountComponent,
  18. render,
  19. } from "@excalidraw/excalidraw/tests/test-utils";
  20. const mouse = new Pointer("mouse");
  21. const createAndSelectTwoRectangles = () => {
  22. UI.clickTool("rectangle");
  23. mouse.down();
  24. mouse.up(100, 100);
  25. UI.clickTool("rectangle");
  26. mouse.down(10, 10);
  27. mouse.up(100, 100);
  28. // Select the first element.
  29. // The second rectangle is already reselected because it was the last element created
  30. mouse.reset();
  31. Keyboard.withModifierKeys({ shift: true }, () => {
  32. mouse.click();
  33. });
  34. };
  35. const createAndSelectTwoRectanglesWithDifferentSizes = () => {
  36. UI.clickTool("rectangle");
  37. mouse.down();
  38. mouse.up(100, 100);
  39. UI.clickTool("rectangle");
  40. mouse.down(10, 10);
  41. mouse.up(110, 110);
  42. // Select the first element.
  43. // The second rectangle is already reselected because it was the last element created
  44. mouse.reset();
  45. Keyboard.withModifierKeys({ shift: true }, () => {
  46. mouse.click();
  47. });
  48. };
  49. describe("aligning", () => {
  50. beforeEach(async () => {
  51. unmountComponent();
  52. mouse.reset();
  53. await act(() => {
  54. return setLanguage(defaultLang);
  55. });
  56. await render(<Excalidraw handleKeyboardGlobally={true} />);
  57. });
  58. it("aligns two objects correctly to the top", () => {
  59. createAndSelectTwoRectangles();
  60. expect(API.getSelectedElements()[0].x).toEqual(0);
  61. expect(API.getSelectedElements()[1].x).toEqual(110);
  62. expect(API.getSelectedElements()[0].y).toEqual(0);
  63. expect(API.getSelectedElements()[1].y).toEqual(110);
  64. Keyboard.withModifierKeys({ ctrl: true, shift: true }, () => {
  65. Keyboard.keyPress(KEYS.ARROW_UP);
  66. });
  67. // Check if x position did not change
  68. expect(API.getSelectedElements()[0].x).toEqual(0);
  69. expect(API.getSelectedElements()[1].x).toEqual(110);
  70. expect(API.getSelectedElements()[0].y).toEqual(0);
  71. expect(API.getSelectedElements()[1].y).toEqual(0);
  72. });
  73. it("aligns two objects correctly to the bottom", () => {
  74. createAndSelectTwoRectangles();
  75. expect(API.getSelectedElements()[0].x).toEqual(0);
  76. expect(API.getSelectedElements()[1].x).toEqual(110);
  77. expect(API.getSelectedElements()[0].y).toEqual(0);
  78. expect(API.getSelectedElements()[1].y).toEqual(110);
  79. Keyboard.withModifierKeys({ ctrl: true, shift: true }, () => {
  80. Keyboard.keyPress(KEYS.ARROW_DOWN);
  81. });
  82. // Check if x position did not change
  83. expect(API.getSelectedElements()[0].x).toEqual(0);
  84. expect(API.getSelectedElements()[1].x).toEqual(110);
  85. expect(API.getSelectedElements()[0].y).toEqual(110);
  86. expect(API.getSelectedElements()[1].y).toEqual(110);
  87. });
  88. it("aligns two objects correctly to the left", () => {
  89. createAndSelectTwoRectangles();
  90. expect(API.getSelectedElements()[0].x).toEqual(0);
  91. expect(API.getSelectedElements()[1].x).toEqual(110);
  92. expect(API.getSelectedElements()[0].y).toEqual(0);
  93. expect(API.getSelectedElements()[1].y).toEqual(110);
  94. Keyboard.withModifierKeys({ ctrl: true, shift: true }, () => {
  95. Keyboard.keyPress(KEYS.ARROW_LEFT);
  96. });
  97. expect(API.getSelectedElements()[0].x).toEqual(0);
  98. expect(API.getSelectedElements()[1].x).toEqual(0);
  99. // Check if y position did not change
  100. expect(API.getSelectedElements()[0].y).toEqual(0);
  101. expect(API.getSelectedElements()[1].y).toEqual(110);
  102. });
  103. it("aligns two objects correctly to the right", () => {
  104. createAndSelectTwoRectangles();
  105. expect(API.getSelectedElements()[0].x).toEqual(0);
  106. expect(API.getSelectedElements()[1].x).toEqual(110);
  107. expect(API.getSelectedElements()[0].y).toEqual(0);
  108. expect(API.getSelectedElements()[1].y).toEqual(110);
  109. Keyboard.withModifierKeys({ ctrl: true, shift: true }, () => {
  110. Keyboard.keyPress(KEYS.ARROW_RIGHT);
  111. });
  112. expect(API.getSelectedElements()[0].x).toEqual(110);
  113. expect(API.getSelectedElements()[1].x).toEqual(110);
  114. // Check if y position did not change
  115. expect(API.getSelectedElements()[0].y).toEqual(0);
  116. expect(API.getSelectedElements()[1].y).toEqual(110);
  117. });
  118. it("centers two objects with different sizes correctly vertically", () => {
  119. createAndSelectTwoRectanglesWithDifferentSizes();
  120. expect(API.getSelectedElements()[0].x).toEqual(0);
  121. expect(API.getSelectedElements()[1].x).toEqual(110);
  122. expect(API.getSelectedElements()[0].y).toEqual(0);
  123. expect(API.getSelectedElements()[1].y).toEqual(110);
  124. API.executeAction(actionAlignVerticallyCentered);
  125. // Check if x position did not change
  126. expect(API.getSelectedElements()[0].x).toEqual(0);
  127. expect(API.getSelectedElements()[1].x).toEqual(110);
  128. expect(API.getSelectedElements()[0].y).toEqual(60);
  129. expect(API.getSelectedElements()[1].y).toEqual(55);
  130. });
  131. it("centers two objects with different sizes correctly horizontally", () => {
  132. createAndSelectTwoRectanglesWithDifferentSizes();
  133. expect(API.getSelectedElements()[0].x).toEqual(0);
  134. expect(API.getSelectedElements()[1].x).toEqual(110);
  135. expect(API.getSelectedElements()[0].y).toEqual(0);
  136. expect(API.getSelectedElements()[1].y).toEqual(110);
  137. API.executeAction(actionAlignHorizontallyCentered);
  138. expect(API.getSelectedElements()[0].x).toEqual(60);
  139. expect(API.getSelectedElements()[1].x).toEqual(55);
  140. // Check if y position did not change
  141. expect(API.getSelectedElements()[0].y).toEqual(0);
  142. expect(API.getSelectedElements()[1].y).toEqual(110);
  143. });
  144. const createAndSelectGroupAndRectangle = () => {
  145. UI.clickTool("rectangle");
  146. mouse.down();
  147. mouse.up(100, 100);
  148. UI.clickTool("rectangle");
  149. mouse.down(0, 0);
  150. mouse.up(100, 100);
  151. // Select the first element.
  152. // The second rectangle is already reselected because it was the last element created
  153. mouse.reset();
  154. Keyboard.withModifierKeys({ shift: true }, () => {
  155. mouse.click();
  156. });
  157. API.executeAction(actionGroup);
  158. mouse.reset();
  159. UI.clickTool("rectangle");
  160. mouse.down(200, 200);
  161. mouse.up(100, 100);
  162. // Add the created group to the current selection
  163. mouse.restorePosition(0, 0);
  164. Keyboard.withModifierKeys({ shift: true }, () => {
  165. mouse.click();
  166. });
  167. };
  168. it("aligns a group with another element correctly to the top", () => {
  169. createAndSelectGroupAndRectangle();
  170. expect(API.getSelectedElements()[0].y).toEqual(0);
  171. expect(API.getSelectedElements()[1].y).toEqual(100);
  172. expect(API.getSelectedElements()[2].y).toEqual(200);
  173. API.executeAction(actionAlignTop);
  174. expect(API.getSelectedElements()[0].y).toEqual(0);
  175. expect(API.getSelectedElements()[1].y).toEqual(100);
  176. expect(API.getSelectedElements()[2].y).toEqual(0);
  177. });
  178. it("aligns a group with another element correctly to the bottom", () => {
  179. createAndSelectGroupAndRectangle();
  180. expect(API.getSelectedElements()[0].y).toEqual(0);
  181. expect(API.getSelectedElements()[1].y).toEqual(100);
  182. expect(API.getSelectedElements()[2].y).toEqual(200);
  183. API.executeAction(actionAlignBottom);
  184. expect(API.getSelectedElements()[0].y).toEqual(100);
  185. expect(API.getSelectedElements()[1].y).toEqual(200);
  186. expect(API.getSelectedElements()[2].y).toEqual(200);
  187. });
  188. it("aligns a group with another element correctly to the left", () => {
  189. createAndSelectGroupAndRectangle();
  190. expect(API.getSelectedElements()[0].x).toEqual(0);
  191. expect(API.getSelectedElements()[1].x).toEqual(100);
  192. expect(API.getSelectedElements()[2].x).toEqual(200);
  193. API.executeAction(actionAlignLeft);
  194. expect(API.getSelectedElements()[0].x).toEqual(0);
  195. expect(API.getSelectedElements()[1].x).toEqual(100);
  196. expect(API.getSelectedElements()[2].x).toEqual(0);
  197. });
  198. it("aligns a group with another element correctly to the right", () => {
  199. createAndSelectGroupAndRectangle();
  200. expect(API.getSelectedElements()[0].x).toEqual(0);
  201. expect(API.getSelectedElements()[1].x).toEqual(100);
  202. expect(API.getSelectedElements()[2].x).toEqual(200);
  203. API.executeAction(actionAlignRight);
  204. expect(API.getSelectedElements()[0].x).toEqual(100);
  205. expect(API.getSelectedElements()[1].x).toEqual(200);
  206. expect(API.getSelectedElements()[2].x).toEqual(200);
  207. });
  208. it("centers a group with another element correctly vertically", () => {
  209. createAndSelectGroupAndRectangle();
  210. expect(API.getSelectedElements()[0].y).toEqual(0);
  211. expect(API.getSelectedElements()[1].y).toEqual(100);
  212. expect(API.getSelectedElements()[2].y).toEqual(200);
  213. API.executeAction(actionAlignVerticallyCentered);
  214. expect(API.getSelectedElements()[0].y).toEqual(50);
  215. expect(API.getSelectedElements()[1].y).toEqual(150);
  216. expect(API.getSelectedElements()[2].y).toEqual(100);
  217. });
  218. it("centers a group with another element correctly horizontally", () => {
  219. createAndSelectGroupAndRectangle();
  220. expect(API.getSelectedElements()[0].x).toEqual(0);
  221. expect(API.getSelectedElements()[1].x).toEqual(100);
  222. expect(API.getSelectedElements()[2].x).toEqual(200);
  223. API.executeAction(actionAlignHorizontallyCentered);
  224. expect(API.getSelectedElements()[0].x).toEqual(50);
  225. expect(API.getSelectedElements()[1].x).toEqual(150);
  226. expect(API.getSelectedElements()[2].x).toEqual(100);
  227. });
  228. const createAndSelectTwoGroups = () => {
  229. UI.clickTool("rectangle");
  230. mouse.down();
  231. mouse.up(100, 100);
  232. UI.clickTool("rectangle");
  233. mouse.down(0, 0);
  234. mouse.up(100, 100);
  235. // Select the first element.
  236. // The second rectangle is already selected because it was the last element created
  237. mouse.reset();
  238. Keyboard.withModifierKeys({ shift: true }, () => {
  239. mouse.click();
  240. });
  241. API.executeAction(actionGroup);
  242. mouse.reset();
  243. UI.clickTool("rectangle");
  244. mouse.down(200, 200);
  245. mouse.up(100, 100);
  246. UI.clickTool("rectangle");
  247. mouse.down();
  248. mouse.up(100, 100);
  249. mouse.restorePosition(200, 200);
  250. Keyboard.withModifierKeys({ shift: true }, () => {
  251. mouse.click();
  252. });
  253. API.executeAction(actionGroup);
  254. // Select the first group.
  255. // The second group is already selected because it was the last group created
  256. mouse.reset();
  257. Keyboard.withModifierKeys({ shift: true }, () => {
  258. mouse.click();
  259. });
  260. };
  261. it("aligns two groups correctly to the top", () => {
  262. createAndSelectTwoGroups();
  263. expect(API.getSelectedElements()[0].y).toEqual(0);
  264. expect(API.getSelectedElements()[1].y).toEqual(100);
  265. expect(API.getSelectedElements()[2].y).toEqual(200);
  266. expect(API.getSelectedElements()[3].y).toEqual(300);
  267. API.executeAction(actionAlignTop);
  268. expect(API.getSelectedElements()[0].y).toEqual(0);
  269. expect(API.getSelectedElements()[1].y).toEqual(100);
  270. expect(API.getSelectedElements()[2].y).toEqual(0);
  271. expect(API.getSelectedElements()[3].y).toEqual(100);
  272. });
  273. it("aligns two groups correctly to the bottom", () => {
  274. createAndSelectTwoGroups();
  275. expect(API.getSelectedElements()[0].y).toEqual(0);
  276. expect(API.getSelectedElements()[1].y).toEqual(100);
  277. expect(API.getSelectedElements()[2].y).toEqual(200);
  278. expect(API.getSelectedElements()[3].y).toEqual(300);
  279. API.executeAction(actionAlignBottom);
  280. expect(API.getSelectedElements()[0].y).toEqual(200);
  281. expect(API.getSelectedElements()[1].y).toEqual(300);
  282. expect(API.getSelectedElements()[2].y).toEqual(200);
  283. expect(API.getSelectedElements()[3].y).toEqual(300);
  284. });
  285. it("aligns two groups correctly to the left", () => {
  286. createAndSelectTwoGroups();
  287. expect(API.getSelectedElements()[0].x).toEqual(0);
  288. expect(API.getSelectedElements()[1].x).toEqual(100);
  289. expect(API.getSelectedElements()[2].x).toEqual(200);
  290. expect(API.getSelectedElements()[3].x).toEqual(300);
  291. API.executeAction(actionAlignLeft);
  292. expect(API.getSelectedElements()[0].x).toEqual(0);
  293. expect(API.getSelectedElements()[1].x).toEqual(100);
  294. expect(API.getSelectedElements()[2].x).toEqual(0);
  295. expect(API.getSelectedElements()[3].x).toEqual(100);
  296. });
  297. it("aligns two groups correctly to the right", () => {
  298. createAndSelectTwoGroups();
  299. expect(API.getSelectedElements()[0].x).toEqual(0);
  300. expect(API.getSelectedElements()[1].x).toEqual(100);
  301. expect(API.getSelectedElements()[2].x).toEqual(200);
  302. expect(API.getSelectedElements()[3].x).toEqual(300);
  303. API.executeAction(actionAlignRight);
  304. expect(API.getSelectedElements()[0].x).toEqual(200);
  305. expect(API.getSelectedElements()[1].x).toEqual(300);
  306. expect(API.getSelectedElements()[2].x).toEqual(200);
  307. expect(API.getSelectedElements()[3].x).toEqual(300);
  308. });
  309. it("centers two groups correctly vertically", () => {
  310. createAndSelectTwoGroups();
  311. expect(API.getSelectedElements()[0].y).toEqual(0);
  312. expect(API.getSelectedElements()[1].y).toEqual(100);
  313. expect(API.getSelectedElements()[2].y).toEqual(200);
  314. expect(API.getSelectedElements()[3].y).toEqual(300);
  315. API.executeAction(actionAlignVerticallyCentered);
  316. expect(API.getSelectedElements()[0].y).toEqual(100);
  317. expect(API.getSelectedElements()[1].y).toEqual(200);
  318. expect(API.getSelectedElements()[2].y).toEqual(100);
  319. expect(API.getSelectedElements()[3].y).toEqual(200);
  320. });
  321. it("centers two groups correctly horizontally", () => {
  322. createAndSelectTwoGroups();
  323. expect(API.getSelectedElements()[0].x).toEqual(0);
  324. expect(API.getSelectedElements()[1].x).toEqual(100);
  325. expect(API.getSelectedElements()[2].x).toEqual(200);
  326. expect(API.getSelectedElements()[3].x).toEqual(300);
  327. API.executeAction(actionAlignHorizontallyCentered);
  328. expect(API.getSelectedElements()[0].x).toEqual(100);
  329. expect(API.getSelectedElements()[1].x).toEqual(200);
  330. expect(API.getSelectedElements()[2].x).toEqual(100);
  331. expect(API.getSelectedElements()[3].x).toEqual(200);
  332. });
  333. const createAndSelectNestedGroupAndRectangle = () => {
  334. UI.clickTool("rectangle");
  335. mouse.down();
  336. mouse.up(100, 100);
  337. UI.clickTool("rectangle");
  338. mouse.down(0, 0);
  339. mouse.up(100, 100);
  340. // Select the first element.
  341. // The second rectangle is already reselected because it was the last element created
  342. mouse.reset();
  343. Keyboard.withModifierKeys({ shift: true }, () => {
  344. mouse.click();
  345. });
  346. // Create first group of rectangles
  347. API.executeAction(actionGroup);
  348. mouse.reset();
  349. UI.clickTool("rectangle");
  350. mouse.down(200, 200);
  351. mouse.up(100, 100);
  352. // Add group to current selection
  353. mouse.restorePosition(0, 0);
  354. Keyboard.withModifierKeys({ shift: true }, () => {
  355. mouse.click();
  356. });
  357. // Create the nested group
  358. API.executeAction(actionGroup);
  359. mouse.reset();
  360. UI.clickTool("rectangle");
  361. mouse.down(300, 300);
  362. mouse.up(100, 100);
  363. // Select the nested group, the rectangle is already selected
  364. mouse.reset();
  365. Keyboard.withModifierKeys({ shift: true }, () => {
  366. mouse.click();
  367. });
  368. };
  369. it("aligns nested group and other element correctly to the top", () => {
  370. createAndSelectNestedGroupAndRectangle();
  371. expect(API.getSelectedElements()[0].y).toEqual(0);
  372. expect(API.getSelectedElements()[1].y).toEqual(100);
  373. expect(API.getSelectedElements()[2].y).toEqual(200);
  374. expect(API.getSelectedElements()[3].y).toEqual(300);
  375. API.executeAction(actionAlignTop);
  376. expect(API.getSelectedElements()[0].y).toEqual(0);
  377. expect(API.getSelectedElements()[1].y).toEqual(100);
  378. expect(API.getSelectedElements()[2].y).toEqual(200);
  379. expect(API.getSelectedElements()[3].y).toEqual(0);
  380. });
  381. it("aligns nested group and other element correctly to the bottom", () => {
  382. createAndSelectNestedGroupAndRectangle();
  383. expect(API.getSelectedElements()[0].y).toEqual(0);
  384. expect(API.getSelectedElements()[1].y).toEqual(100);
  385. expect(API.getSelectedElements()[2].y).toEqual(200);
  386. expect(API.getSelectedElements()[3].y).toEqual(300);
  387. API.executeAction(actionAlignBottom);
  388. expect(API.getSelectedElements()[0].y).toEqual(100);
  389. expect(API.getSelectedElements()[1].y).toEqual(200);
  390. expect(API.getSelectedElements()[2].y).toEqual(300);
  391. expect(API.getSelectedElements()[3].y).toEqual(300);
  392. });
  393. it("aligns nested group and other element correctly to the left", () => {
  394. createAndSelectNestedGroupAndRectangle();
  395. expect(API.getSelectedElements()[0].x).toEqual(0);
  396. expect(API.getSelectedElements()[1].x).toEqual(100);
  397. expect(API.getSelectedElements()[2].x).toEqual(200);
  398. expect(API.getSelectedElements()[3].x).toEqual(300);
  399. API.executeAction(actionAlignLeft);
  400. expect(API.getSelectedElements()[0].x).toEqual(0);
  401. expect(API.getSelectedElements()[1].x).toEqual(100);
  402. expect(API.getSelectedElements()[2].x).toEqual(200);
  403. expect(API.getSelectedElements()[3].x).toEqual(0);
  404. });
  405. it("aligns nested group and other element correctly to the right", () => {
  406. createAndSelectNestedGroupAndRectangle();
  407. expect(API.getSelectedElements()[0].x).toEqual(0);
  408. expect(API.getSelectedElements()[1].x).toEqual(100);
  409. expect(API.getSelectedElements()[2].x).toEqual(200);
  410. expect(API.getSelectedElements()[3].x).toEqual(300);
  411. API.executeAction(actionAlignRight);
  412. expect(API.getSelectedElements()[0].x).toEqual(100);
  413. expect(API.getSelectedElements()[1].x).toEqual(200);
  414. expect(API.getSelectedElements()[2].x).toEqual(300);
  415. expect(API.getSelectedElements()[3].x).toEqual(300);
  416. });
  417. it("centers nested group and other element correctly vertically", () => {
  418. createAndSelectNestedGroupAndRectangle();
  419. expect(API.getSelectedElements()[0].y).toEqual(0);
  420. expect(API.getSelectedElements()[1].y).toEqual(100);
  421. expect(API.getSelectedElements()[2].y).toEqual(200);
  422. expect(API.getSelectedElements()[3].y).toEqual(300);
  423. API.executeAction(actionAlignVerticallyCentered);
  424. expect(API.getSelectedElements()[0].y).toEqual(50);
  425. expect(API.getSelectedElements()[1].y).toEqual(150);
  426. expect(API.getSelectedElements()[2].y).toEqual(250);
  427. expect(API.getSelectedElements()[3].y).toEqual(150);
  428. });
  429. it("centers nested group and other element correctly horizontally", () => {
  430. createAndSelectNestedGroupAndRectangle();
  431. expect(API.getSelectedElements()[0].x).toEqual(0);
  432. expect(API.getSelectedElements()[1].x).toEqual(100);
  433. expect(API.getSelectedElements()[2].x).toEqual(200);
  434. expect(API.getSelectedElements()[3].x).toEqual(300);
  435. API.executeAction(actionAlignHorizontallyCentered);
  436. expect(API.getSelectedElements()[0].x).toEqual(50);
  437. expect(API.getSelectedElements()[1].x).toEqual(150);
  438. expect(API.getSelectedElements()[2].x).toEqual(250);
  439. expect(API.getSelectedElements()[3].x).toEqual(150);
  440. });
  441. });