align.test.tsx 19 KB

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