custom-group-spec.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763
  1. /*
  2. * @Author: moyee
  3. * @Date: 2019-07-31 11:54:41
  4. * @LastEditors: moyee
  5. * @LastEditTime: 2019-08-23 14:16:27
  6. * @Description: Group单测文件
  7. */
  8. const expect = require('chai').expect;
  9. const G6 = require('../../../../src');
  10. const Util = G6.Util;
  11. const { groupBy } = require('lodash');
  12. const div = document.createElement('div');
  13. div.id = 'graph-group-spec';
  14. document.body.appendChild(div);
  15. G6.registerNode('circleNode', {
  16. drawShape(cfg, group) {
  17. const keyShape = group.addShape('circle', {
  18. attrs: {
  19. x: 0,
  20. y: 0,
  21. r: 30,
  22. fill: '#87e8de'
  23. }
  24. });
  25. return keyShape;
  26. }
  27. }, 'circle');
  28. describe.only('signle layer group', () => {
  29. it('render signle group test', () => {
  30. const graph = new G6.Graph({
  31. container: div,
  32. width: 1500,
  33. height: 1000,
  34. pixelRatio: 2,
  35. modes: {
  36. default: [ 'drag-group' ]
  37. },
  38. defaultNode: {
  39. shape: 'circleNode'
  40. },
  41. defaultEdge: {
  42. color: '#bae7ff'
  43. }
  44. });
  45. const nodes = [
  46. {
  47. id: 'node1',
  48. label: 'node1',
  49. groupId: 'group1',
  50. x: 100,
  51. y: 100
  52. },
  53. {
  54. id: 'node2',
  55. label: 'node2',
  56. groupId: 'group1',
  57. x: 150,
  58. y: 100
  59. },
  60. {
  61. id: 'node3',
  62. label: 'node3',
  63. groupId: 'group2',
  64. x: 300,
  65. y: 100
  66. },
  67. {
  68. id: 'node7',
  69. groupId: 'p1',
  70. x: 200,
  71. y: 200
  72. },
  73. {
  74. id: 'node6',
  75. groupId: 'bym',
  76. label: 'rect',
  77. x: 100,
  78. y: 300,
  79. shape: 'rect'
  80. },
  81. {
  82. id: 'node9',
  83. label: 'noGroup',
  84. x: 300,
  85. y: 210
  86. }
  87. ];
  88. const data = {
  89. nodes
  90. };
  91. graph.data(data);
  92. graph.render();
  93. const groupControll = graph.get('customGroupControll');
  94. const group = graph.get('customGroup');
  95. const children = group.get('children');
  96. // group的数量
  97. expect(children.length).equal(4);
  98. // 每个group的圆心坐标
  99. const nodesInGroup = groupBy(data.nodes, 'groupId');
  100. for (const node in nodesInGroup) {
  101. if (node === 'undefined') {
  102. continue;
  103. }
  104. const currentNodes = nodesInGroup[node];
  105. const nodeIds = currentNodes.map(nodeId => nodeId.id);
  106. const { x, y, width, height } = groupControll.calculationGroupPosition(nodeIds);
  107. const r = width > height ? width / 2 : height / 2;
  108. const cx = (width + 2 * x) / 2;
  109. const cy = (height + 2 * y) / 2;
  110. const groupShape = groupControll.getDeletageGroupById(node);
  111. const { groupStyle } = groupShape;
  112. expect(groupStyle.x).eql(cx);
  113. expect(groupStyle.y).eql(cy);
  114. expect(groupStyle.r).eql(r);
  115. }
  116. graph.destroy();
  117. expect(graph.destroyed).to.be.true;
  118. });
  119. it('setGroupStyle', () => {
  120. const graph = new G6.Graph({
  121. container: div,
  122. width: 1500,
  123. height: 1000,
  124. pixelRatio: 2,
  125. modes: {
  126. default: [ 'drag-group' ]
  127. },
  128. defaultNode: {
  129. shape: 'circleNode'
  130. },
  131. defaultEdge: {
  132. color: '#bae7ff'
  133. }
  134. });
  135. const data = {
  136. nodes: [
  137. {
  138. id: 'node1',
  139. label: 'node1',
  140. groupId: 'group1',
  141. x: 100,
  142. y: 100
  143. },
  144. {
  145. id: 'node2',
  146. label: 'node2',
  147. groupId: 'group1',
  148. x: 150,
  149. y: 100
  150. },
  151. {
  152. id: 'node3',
  153. label: 'node3',
  154. groupId: 'group2',
  155. x: 300,
  156. y: 100
  157. },
  158. {
  159. id: 'node7',
  160. groupId: 'p1',
  161. x: 200,
  162. y: 200
  163. },
  164. {
  165. id: 'node6',
  166. groupId: 'bym',
  167. label: 'rect',
  168. x: 100,
  169. y: 300,
  170. shape: 'rect'
  171. },
  172. {
  173. id: 'node9',
  174. label: 'noGroup',
  175. x: 300,
  176. y: 210
  177. }
  178. ]
  179. };
  180. graph.data(data);
  181. graph.render();
  182. const groupControll = graph.get('customGroupControll');
  183. const { nodeGroup } = groupControll.getDeletageGroupById('group2');
  184. const keyShape = nodeGroup.get('keyShape');
  185. groupControll.setGroupStyle(keyShape, 'hover');
  186. // 这里的hover样式和定义custom group时指定的一样
  187. const hover = {
  188. stroke: '#faad14',
  189. fill: '#ffe58f',
  190. fillOpacity: 0.3,
  191. opacity: 0.3,
  192. lineWidth: 3
  193. };
  194. expect(keyShape.attr('stroke')).eql(hover.stroke);
  195. expect(keyShape.attr('fill')).eql(hover.fill);
  196. expect(keyShape.attr('fillOpacity')).eql(hover.fillOpacity);
  197. expect(keyShape.attr('opacity')).eql(hover.opacity);
  198. expect(keyShape.attr('lineWidth')).eql(hover.lineWidth);
  199. expect(keyShape.attr('customStyle')).eql(undefined);
  200. // 设置自定义样式属性customStyle
  201. groupControll.setGroupStyle(keyShape, { customStyle: true });
  202. expect(keyShape.attr('customStyle')).eql(true);
  203. graph.destroy();
  204. expect(graph.destroyed).to.be.true;
  205. });
  206. it('setGroupOriginBBox / getGroupOriginBBox', () => {
  207. const graph = new G6.Graph({
  208. container: div,
  209. width: 1500,
  210. height: 1000,
  211. pixelRatio: 2,
  212. modes: {
  213. default: [ 'drag-group' ]
  214. },
  215. defaultNode: {
  216. shape: 'circleNode'
  217. },
  218. defaultEdge: {
  219. color: '#bae7ff'
  220. }
  221. });
  222. const nodes = [
  223. {
  224. id: 'node1',
  225. label: 'node1',
  226. groupId: 'group1',
  227. x: 100,
  228. y: 100
  229. },
  230. {
  231. id: 'node2',
  232. label: 'node2',
  233. groupId: 'group1',
  234. x: 150,
  235. y: 100
  236. },
  237. {
  238. id: 'node3',
  239. label: 'node3',
  240. groupId: 'group2',
  241. x: 300,
  242. y: 100
  243. },
  244. {
  245. id: 'node7',
  246. groupId: 'p1',
  247. x: 200,
  248. y: 200
  249. },
  250. {
  251. id: 'node6',
  252. groupId: 'bym',
  253. label: 'rect',
  254. x: 100,
  255. y: 300,
  256. shape: 'rect'
  257. },
  258. {
  259. id: 'node9',
  260. label: 'noGroup',
  261. x: 300,
  262. y: 210
  263. }
  264. ];
  265. const data = {
  266. nodes
  267. };
  268. graph.data(data);
  269. graph.render();
  270. const groupControll = graph.get('customGroupControll');
  271. const { nodeGroup } = groupControll.getDeletageGroupById('group1');
  272. const keyShape = nodeGroup.get('keyShape');
  273. const bbox = keyShape.getBBox();
  274. // 调用setGroupOriginBBox方法存储group1的bbox
  275. groupControll.setGroupOriginBBox('group1', bbox);
  276. const groupBBox = groupControll.getGroupOriginBBox('group1');
  277. expect(groupBBox).eql(bbox);
  278. graph.destroy();
  279. expect(graph.destroyed).to.be.true;
  280. });
  281. it('setDeletageGroupByStyle / getDeletageGroupById', () => {
  282. const graph = new G6.Graph({
  283. container: div,
  284. width: 1500,
  285. height: 1000,
  286. pixelRatio: 2,
  287. modes: {
  288. default: [ 'drag-group' ]
  289. },
  290. defaultNode: {
  291. shape: 'circleNode'
  292. },
  293. defaultEdge: {
  294. color: '#bae7ff'
  295. }
  296. });
  297. const nodes = [
  298. {
  299. id: 'node1',
  300. label: 'node1',
  301. groupId: 'group1',
  302. x: 100,
  303. y: 100
  304. },
  305. {
  306. id: 'node2',
  307. label: 'node2',
  308. groupId: 'group1',
  309. x: 150,
  310. y: 100
  311. },
  312. {
  313. id: 'node3',
  314. label: 'node3',
  315. groupId: 'group2',
  316. x: 300,
  317. y: 100
  318. },
  319. {
  320. id: 'node7',
  321. groupId: 'p1',
  322. x: 200,
  323. y: 200
  324. },
  325. {
  326. id: 'node6',
  327. groupId: 'bym',
  328. label: 'rect',
  329. x: 100,
  330. y: 300,
  331. shape: 'rect'
  332. },
  333. {
  334. id: 'node9',
  335. label: 'noGroup',
  336. x: 300,
  337. y: 210
  338. }
  339. ];
  340. const data = {
  341. nodes
  342. };
  343. graph.data(data);
  344. graph.render();
  345. const groupControll = graph.get('customGroupControll');
  346. // setDeletageGroupByStyle方法是在创建的时候就调用的,这里测试创建时候存进去的值通过getDeletageGroupById取出来后是否相同
  347. const group2 = groupControll.getDeletageGroupById('group2');
  348. const { groupStyle, nodeGroup } = group2;
  349. expect(nodeGroup).not.null;
  350. expect(nodeGroup.get('id')).eql('group2');
  351. expect(nodeGroup.get('destroyed')).to.be.false;
  352. const nodeInGroup2 = data.nodes.filter(node => node.groupId === 'group2').map(node => node.id);
  353. const { width, height, x, y } = groupControll.calculationGroupPosition(nodeInGroup2);
  354. const r = width > height ? width / 2 : height / 2;
  355. const cx = (width + 2 * x) / 2;
  356. const cy = (height + 2 * y) / 2;
  357. expect(groupStyle.x).eql(cx);
  358. expect(groupStyle.y).eql(cy);
  359. expect(groupStyle.r).eql(r);
  360. expect(groupStyle.width).eql(width);
  361. expect(groupStyle.height).eql(height);
  362. // 通过不存在的groupId查询
  363. const notGroup = groupControll.getDeletageGroupById('group5');
  364. expect(notGroup).to.be.undefined;
  365. graph.destroy();
  366. expect(graph.destroyed).to.be.true;
  367. });
  368. it('collapseExpandGroup', () => {
  369. const graph = new G6.Graph({
  370. container: div,
  371. width: 1500,
  372. height: 1000,
  373. pixelRatio: 2,
  374. modes: {
  375. default: [ 'drag-group' ]
  376. },
  377. defaultNode: {
  378. shape: 'circleNode'
  379. },
  380. defaultEdge: {
  381. color: '#bae7ff'
  382. }
  383. });
  384. const data = {
  385. nodes: [
  386. {
  387. id: 'node1',
  388. label: 'node1',
  389. groupId: 'group1',
  390. x: 100,
  391. y: 100
  392. },
  393. {
  394. id: 'node2',
  395. label: 'node2',
  396. groupId: 'group1',
  397. x: 150,
  398. y: 100
  399. },
  400. {
  401. id: 'node3',
  402. label: 'node3',
  403. groupId: 'group2',
  404. x: 300,
  405. y: 100
  406. },
  407. {
  408. id: 'node7',
  409. groupId: 'p1',
  410. x: 200,
  411. y: 200
  412. },
  413. {
  414. id: 'node6',
  415. groupId: 'bym',
  416. label: 'rect',
  417. x: 100,
  418. y: 300,
  419. shape: 'rect'
  420. },
  421. {
  422. id: 'node9',
  423. label: 'noGroup',
  424. x: 300,
  425. y: 210
  426. }
  427. ]
  428. };
  429. graph.data(data);
  430. graph.render();
  431. const groupControll = graph.get('customGroupControll');
  432. const { nodeGroup } = groupControll.getDeletageGroupById('group1');
  433. expect(nodeGroup.get('hasHidden')).to.be.undefined;
  434. groupControll.collapseExpandGroup('group1');
  435. expect(nodeGroup.get('hasHidden')).to.be.true;
  436. groupControll.collapseExpandGroup('group1');
  437. expect(nodeGroup.get('hasHidden')).to.be.false;
  438. graph.destroy();
  439. expect(graph.destroyed).to.be.true;
  440. });
  441. it('collapseGroup / expandGroup', () => {
  442. const graph = new G6.Graph({
  443. container: div,
  444. width: 1500,
  445. height: 1000,
  446. pixelRatio: 2,
  447. modes: {
  448. default: [ 'drag-group' ]
  449. },
  450. defaultNode: {
  451. shape: 'circleNode'
  452. },
  453. defaultEdge: {
  454. color: '#bae7ff'
  455. }
  456. });
  457. const data = {
  458. nodes: [
  459. {
  460. id: 'node1',
  461. label: 'node1',
  462. groupId: 'group1',
  463. x: 100,
  464. y: 100
  465. },
  466. {
  467. id: 'node2',
  468. label: 'node2',
  469. groupId: 'group1',
  470. x: 150,
  471. y: 100
  472. },
  473. {
  474. id: 'node3',
  475. label: 'node3',
  476. groupId: 'group2',
  477. x: 300,
  478. y: 100
  479. },
  480. {
  481. id: 'node7',
  482. groupId: 'p1',
  483. x: 200,
  484. y: 200
  485. },
  486. {
  487. id: 'node6',
  488. groupId: 'bym',
  489. label: 'rect',
  490. x: 100,
  491. y: 300,
  492. shape: 'rect'
  493. },
  494. {
  495. id: 'node9',
  496. label: 'noGroup',
  497. x: 300,
  498. y: 210
  499. }
  500. ]
  501. };
  502. graph.data(data);
  503. graph.render();
  504. const groupControll = graph.get('customGroupControll');
  505. const nodes = graph.getNodes();
  506. const groupNodes = nodes.filter(node => {
  507. const model = node.getModel();
  508. return model.groupId === 'group1';
  509. });
  510. // 没有收起群组之前,所有节点都应该是显示状态
  511. for (const node of groupNodes) {
  512. const isVisible = node.isVisible();
  513. expect(isVisible).to.be.true;
  514. }
  515. groupControll.collapseGroup('group1');
  516. // group收起后,隐藏所有节点
  517. for (const node of groupNodes) {
  518. const isVisible = node.isVisible();
  519. expect(isVisible).to.be.false;
  520. }
  521. // 同时检测之前的group是否正确
  522. const { nodeGroup, groupStyle } = groupControll.getDeletageGroupById('group1');
  523. const keyShape = nodeGroup.get('keyShape');
  524. // 延迟下判断,因为收起会有一个动画效果
  525. setTimeout(() => {
  526. expect(keyShape.attr('r')).eql(30);
  527. expect(keyShape.attr('x')).eql(groupStyle.x);
  528. expect(keyShape.attr('y')).eql(groupStyle.y);
  529. }, 2000);
  530. // group 展开
  531. groupControll.expandGroup('group1');
  532. setTimeout(() => {
  533. // 展开后,所有node都是显示状态
  534. for (const node of groupNodes) {
  535. const isVisible = node.isVisible();
  536. expect(isVisible).to.be.true;
  537. }
  538. expect(keyShape.attr('r')).eql(30);
  539. expect(keyShape.attr('x')).eql(groupStyle.x);
  540. expect(keyShape.attr('y')).eql(groupStyle.y);
  541. const nodeIds = groupNodes.map(node => {
  542. const model = node.getModel();
  543. return model.id;
  544. });
  545. const { width, height } = groupControll.calculationGroupPosition(nodeIds);
  546. expect(width).eql(groupStyle.width);
  547. expect(height).eql(groupStyle.height);
  548. graph.destroy();
  549. expect(graph.destroyed).to.be.true;
  550. }, 5500);
  551. });
  552. });
  553. describe('nesting layer group', () => {
  554. it('render nesting layer group', () => {
  555. const data = {
  556. nodes: [
  557. {
  558. id: 'node6',
  559. groupId: 'group3',
  560. label: 'rect',
  561. x: 100,
  562. y: 300
  563. },
  564. {
  565. id: 'node1',
  566. label: 'fck',
  567. groupId: 'group1',
  568. x: 100,
  569. y: 100
  570. },
  571. {
  572. id: 'node9',
  573. label: 'noGroup1',
  574. groupId: 'p1',
  575. x: 300,
  576. y: 210
  577. },
  578. {
  579. id: 'node2',
  580. label: 'node2',
  581. groupId: 'group1',
  582. x: 150,
  583. y: 200
  584. },
  585. {
  586. id: 'node3',
  587. label: 'node3',
  588. groupId: 'group2',
  589. x: 300,
  590. y: 100
  591. },
  592. {
  593. id: 'node7',
  594. groupId: 'p1',
  595. label: 'node7-p1',
  596. x: 200,
  597. y: 200
  598. },
  599. {
  600. id: 'node10',
  601. label: 'noGroup',
  602. groupId: 'p2',
  603. x: 300,
  604. y: 210
  605. }
  606. ],
  607. edges: [
  608. {
  609. source: 'node1',
  610. target: 'node2'
  611. },
  612. {
  613. source: 'node2',
  614. target: 'node3'
  615. }
  616. ],
  617. groups: [
  618. {
  619. id: 'group1',
  620. title: '1',
  621. parentId: 'p1'
  622. },
  623. {
  624. id: 'group2',
  625. title: '2',
  626. parentId: 'p1'
  627. },
  628. {
  629. id: 'group3',
  630. title: '2',
  631. parentId: 'p2'
  632. },
  633. {
  634. id: 'p1',
  635. title: '3'
  636. },
  637. {
  638. id: 'p2',
  639. title: '3'
  640. }
  641. ]
  642. };
  643. const graph = new G6.Graph({
  644. container: div,
  645. width: 1500,
  646. height: 1000,
  647. pixelRatio: 2,
  648. modes: {
  649. default: [ 'drag-group' ]
  650. },
  651. defaultNode: {
  652. shape: 'circleNode'
  653. },
  654. defaultEdge: {
  655. color: '#bae7ff'
  656. }
  657. });
  658. graph.data(data);
  659. graph.render();
  660. const groupControll = graph.get('customGroupControll');
  661. graph.data(data);
  662. graph.render();
  663. const { groups } = graph.save();
  664. expect(groups.length).equal(5);
  665. // 渲染的每个group的位置和坐标是否和计算的一致
  666. const groupNodes = Util.getAllNodeInGroups(data);
  667. for (const groupId in groupNodes) {
  668. const nodeIds = groupNodes[groupId];
  669. const { x, y, width, height } = groupControll.calculationGroupPosition(nodeIds);
  670. const r = width > height ? width / 2 : height / 2;
  671. const cx = (width + 2 * x) / 2;
  672. const cy = (height + 2 * y) / 2;
  673. const groupShape = groupControll.getDeletageGroupById(groupId);
  674. const { groupStyle } = groupShape;
  675. expect(groupStyle.x).eql(cx);
  676. expect(groupStyle.y).eql(cy);
  677. expect(groupStyle.r).eql(r);
  678. }
  679. // 指定groupId,验证渲染后的位置是否正确
  680. const shape = groupControll.getDeletageGroupById('group2');
  681. const shapeStyle = shape.groupStyle;
  682. expect(shapeStyle.r).eql(30.5);
  683. expect(shapeStyle.x).eql(299.5);
  684. expect(shapeStyle.y).eql(99.5);
  685. graph.destroy();
  686. expect(graph.destroyed).to.be.true;
  687. });
  688. });