Skip to content

Commit 30304b7

Browse files
authored
Add the ability to make link cross multiple points across its route from source node to target node (#378)
* Add link.breakPoints * Add link.breakPoints Tests * Add link.breakPoints Docs * remove docs edit, seperate radius and calcRadiusFn * buildLinkPathDefinition params changes
1 parent f273eff commit 30304b7

5 files changed

Lines changed: 92 additions & 39 deletions

File tree

src/components/graph/graph.builder.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ function buildLinkProps(link, nodes, links, config, linkCallbacks, highlightedNo
127127
config,
128128
strokeWidth
129129
);
130-
const d = buildLinkPathDefinition(sourceCoords, targetCoords, type);
130+
131+
const d = buildLinkPathDefinition(sourceCoords, targetCoords, type, link.breakPoints);
131132

132133
return {
133134
className: CONST.LINK_CLASS_NAME,

src/components/link/link.helper.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,26 @@ function getRadiusStrategy(type) {
6464
* @param {Object} sourceCoords - link sourceCoords
6565
* @param {Object} targetCoords - link targetCoords
6666
* @param {string} type - the link line type
67+
* @param {Array.<Object>} breakPoints - additional set of points that the link will cross
6768
* @returns {string} the path definition for the requested link
6869
* @memberof Link/helper
6970
*/
70-
function buildLinkPathDefinition(sourceCoords = {}, targetCoords = {}, type = LINE_TYPES.STRAIGHT) {
71+
function buildLinkPathDefinition(sourceCoords = {}, targetCoords = {}, type = LINE_TYPES.STRAIGHT, breakPoints = []) {
7172
const { x: sx, y: sy } = sourceCoords;
72-
const { x: tx, y: ty } = targetCoords;
7373
const validType = LINE_TYPES[type] || LINE_TYPES.STRAIGHT;
74-
const radius = getRadiusStrategy(validType)(sx, sy, tx, ty);
74+
const calcRadiusFn = getRadiusStrategy(validType);
7575

76-
return `M${sx},${sy}A${radius},${radius} 0 0,1 ${tx},${ty}`;
76+
const restOfLinkPoints = [...breakPoints, targetCoords];
77+
const restOfLinkPath = restOfLinkPoints
78+
.map(({ x, y }, i) => {
79+
const { x: px, y: py } = i > 0 ? restOfLinkPoints[i - 1] : sourceCoords;
80+
const radius = calcRadiusFn(px, py, x, y);
81+
82+
return ` A${radius},${radius} 0 0,1 ${x},${y}`;
83+
})
84+
.join("");
85+
86+
return `M${sx},${sy}${restOfLinkPath}`;
7787
}
7888

7989
export { buildLinkPathDefinition };

test/graph/__snapshots__/graph.snapshot.spec.js.snap

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
118118
<g>
119119
<path
120120
className="link"
121-
d="M402.6029494893985485,200-4.685309080917388A0,0 0 0,1 137.39705051060145,24.68530908091739"
121+
d="M402.6029494893985485,200-4.685309080917388 A0,0 0 0,1 137.39705051060145,24.68530908091739"
122122
id="Harry,Sally"
123123
onClick={[Function]}
124124
onContextMenu={[Function]}
@@ -138,7 +138,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
138138
<g>
139139
<path
140140
className="link"
141-
d="M40-1.1627040942275513,200-5.232168424023981A0,0 0 0,1 21.16270409422755,115.23216842402398"
141+
d="M40-1.1627040942275513,200-5.232168424023981 A0,0 0 0,1 21.16270409422755,115.23216842402398"
142142
id="Harry,Mario"
143143
onClick={[Function]}
144144
onContextMenu={[Function]}
@@ -158,7 +158,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
158158
<g>
159159
<path
160160
className="link"
161-
d="M302.3591551111937417,1844.812676426835234A0,0 0 0,1 302.64084488880627,740.1873235731648"
161+
d="M302.3591551111937417,1844.812676426835234 A0,0 0 0,1 302.64084488880627,740.1873235731648"
162162
id="Sarah,Alice"
163163
onClick={[Function]}
164164
onContextMenu={[Function]}
@@ -178,7 +178,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
178178
<g>
179179
<path
180180
className="link"
181-
d="M2001.230871970649426,3005.2165526375142335A0,0 0 0,1 303.76912802935055,739.7834473624857"
181+
d="M2001.230871970649426,3005.2165526375142335 A0,0 0 0,1 303.76912802935055,739.7834473624857"
182182
id="Eveie,Alice"
183183
onClick={[Function]}
184184
onContextMenu={[Function]}
@@ -198,7 +198,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
198198
<g>
199199
<path
200200
className="link"
201-
d="M1201.9451766013189067,2704.994372354737734A0,0 0 0,1 303.0548233986811,740.0056276452623"
201+
d="M1201.9451766013189067,2704.994372354737734 A0,0 0 0,1 303.0548233986811,740.0056276452623"
202202
id="Peter,Alice"
203203
onClick={[Function]}
204204
onContextMenu={[Function]}
@@ -218,7 +218,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
218218
<g>
219219
<path
220220
className="link"
221-
d="M202.194669099098748,1104.889876764658614A0,0 0 0,1 302.80533090090125,740.1101232353413"
221+
d="M202.194669099098748,1104.889876764658614 A0,0 0 0,1 302.80533090090125,740.1101232353413"
222222
id="Mario,Alice"
223223
onClick={[Function]}
224224
onContextMenu={[Function]}
@@ -238,7 +238,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
238238
<g>
239239
<path
240240
className="link"
241-
d="M1903.4607726849715568,6094.092739870922885A0,0 0 0,1 301.5392273150284,740.9072601290771"
241+
d="M1903.4607726849715568,6094.092739870922885 A0,0 0 0,1 301.5392273150284,740.9072601290771"
242242
id="James,Alice"
243243
onClick={[Function]}
244244
onContextMenu={[Function]}
@@ -258,7 +258,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
258258
<g>
259259
<path
260260
className="link"
261-
d="M405.359080979074426,2000.0878537865422037A0,0 0 0,1 95.64091902092558,200.9121462134578"
261+
d="M405.359080979074426,2000.0878537865422037 A0,0 0 0,1 95.64091902092558,200.9121462134578"
262262
id="Harry,Carol"
263263
onClick={[Function]}
264264
onContextMenu={[Function]}
@@ -278,7 +278,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
278278
<g>
279279
<path
280280
className="link"
281-
d="M40-5.359801043703684,2000A0,0 0 0,1 7.359801043703684,200"
281+
d="M40-5.359801043703684,2000 A0,0 0 0,1 7.359801043703684,200"
282282
id="Harry,Nicky"
283283
onClick={[Function]}
284284
onContextMenu={[Function]}
@@ -298,7 +298,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
298298
<g>
299299
<path
300300
className="link"
301-
d="M404-3.868474145942595,404-3.709767514314181A0,0 0 0,1 17.868474145942596,33.70976751431418"
301+
d="M404-3.868474145942595,404-3.709767514314181 A0,0 0 0,1 17.868474145942596,33.70976751431418"
302302
id="Bobby,Frank"
303303
onClick={[Function]}
304304
onContextMenu={[Function]}
@@ -318,7 +318,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
318318
<g>
319319
<path
320320
className="link"
321-
d="M305-2.194669099098748,745-4.889876764658614A0,0 0 0,1 22.194669099098746,114.88987676465861"
321+
d="M305-2.194669099098748,745-4.889876764658614 A0,0 0 0,1 22.194669099098746,114.88987676465861"
322322
id="Alice,Mario"
323323
onClick={[Function]}
324324
onContextMenu={[Function]}
@@ -338,7 +338,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
338338
<g>
339339
<path
340340
className="link"
341-
d="M40-2.472758879369286,2004.755305537248627A0,0 0 0,1 16.472758879369287,245.24469446275137"
341+
d="M40-2.472758879369286,2004.755305537248627 A0,0 0 0,1 16.472758879369287,245.24469446275137"
342342
id="Harry,Lynne"
343343
onClick={[Function]}
344344
onContextMenu={[Function]}
@@ -358,7 +358,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
358358
<g>
359359
<path
360360
className="link"
361-
d="M301.8884174089776617,1845.016108742596914A0,0 0 0,1 188.11158259102234,603.9838912574031"
361+
d="M301.8884174089776617,1845.016108742596914 A0,0 0 0,1 188.11158259102234,603.9838912574031"
362362
id="Sarah,James"
363363
onClick={[Function]}
364364
onContextMenu={[Function]}
@@ -378,7 +378,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
378378
<g>
379379
<path
380380
className="link"
381-
d="M142.4079147156322325,2594.788466764041372A0,0 0 0,1 187.59208528436776,604.2115332359587"
381+
d="M142.4079147156322325,2594.788466764041372 A0,0 0 0,1 187.59208528436776,604.2115332359587"
382382
id="Roger,James"
383383
onClick={[Function]}
384384
onContextMenu={[Function]}
@@ -398,7 +398,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
398398
<g>
399399
<path
400400
className="link"
401-
d="M409-4.798325813522697,5002.388207824995315A0,0 0 0,1 194.7983258135227,606.6117921750047"
401+
d="M409-4.798325813522697,5002.388207824995315 A0,0 0 0,1 194.7983258135227,606.6117921750047"
402402
id="Maddy,James"
403403
onClick={[Function]}
404404
onContextMenu={[Function]}
@@ -418,7 +418,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
418418
<g>
419419
<path
420420
className="link"
421-
d="M520-5.1761004847648815,1231.3912048733755413A0,0 0 0,1 19.17610048476488,257.60879512662444"
421+
d="M520-5.1761004847648815,1231.3912048733755413 A0,0 0 0,1 19.17610048476488,257.60879512662444"
422422
id="Sonny,Roger"
423423
onClick={[Function]}
424424
onContextMenu={[Function]}
@@ -438,7 +438,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
438438
<g>
439439
<path
440440
className="link"
441-
d="M190-2.4079147156322325,609-4.788466764041372A0,0 0 0,1 16.40791471563223,263.78846676404135"
441+
d="M190-2.4079147156322325,609-4.788466764041372 A0,0 0 0,1 16.40791471563223,263.78846676404135"
442442
id="James,Roger"
443443
onClick={[Function]}
444444
onContextMenu={[Function]}
@@ -458,7 +458,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
458458
<g>
459459
<path
460460
className="link"
461-
d="M305-1.9451766013189067,745-4.994372354737734A0,0 0 0,1 121.9451766013189,274.9943723547377"
461+
d="M305-1.9451766013189067,745-4.994372354737734 A0,0 0 0,1 121.9451766013189,274.9943723547377"
462462
id="Alice,Peter"
463463
onClick={[Function]}
464464
onContextMenu={[Function]}
@@ -478,7 +478,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
478478
<g>
479479
<path
480480
className="link"
481-
d="M142.0922556386763027,204.934565185557317A0,0 0 0,1 117.9077443613237,265.0654348144427"
481+
d="M142.0922556386763027,204.934565185557317 A0,0 0 0,1 117.9077443613237,265.0654348144427"
482482
id="Johan,Peter"
483483
onClick={[Function]}
484484
onContextMenu={[Function]}
@@ -498,7 +498,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
498498
<g>
499499
<path
500500
className="link"
501-
d="M305-1.230871970649426,745-5.2165526375142335A0,0 0 0,1 201.23087197064942,305.2165526375142"
501+
d="M305-1.230871970649426,745-5.2165526375142335 A0,0 0 0,1 201.23087197064942,305.2165526375142"
502502
id="Alice,Eveie"
503503
onClick={[Function]}
504504
onContextMenu={[Function]}
@@ -518,7 +518,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
518518
<g>
519519
<path
520520
className="link"
521-
d="M404.545102194865424,2002.8406888717908902A0,0 0 0,1 195.45489780513458,297.1593111282091"
521+
d="M404.545102194865424,2002.8406888717908902 A0,0 0 0,1 195.45489780513458,297.1593111282091"
522522
id="Harry,Eveie"
523523
onClick={[Function]}
524524
onContextMenu={[Function]}
@@ -538,7 +538,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
538538
<g>
539539
<path
540540
className="link"
541-
d="M200-4.545102194865424,300-2.8406888717908902A0,0 0 0,1 44.54510219486542,202.8406888717909"
541+
d="M200-4.545102194865424,300-2.8406888717908902 A0,0 0 0,1 44.54510219486542,202.8406888717909"
542542
id="Eveie,Harry"
543543
onClick={[Function]}
544544
onContextMenu={[Function]}
@@ -558,7 +558,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
558558
<g>
559559
<path
560560
className="link"
561-
d="M203.3134907219103313,14.212866775000279A0,0 0 0,1 86.68650927808967,85.78713322499972"
561+
d="M203.3134907219103313,14.212866775000279 A0,0 0 0,1 86.68650927808967,85.78713322499972"
562562
id="Henry,Mikey"
563563
onClick={[Function]}
564564
onContextMenu={[Function]}
@@ -578,7 +578,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
578578
<g>
579579
<path
580580
className="link"
581-
d="M900,656-5.359801043703684A0,0 0 0,1 90,95.35980104370368"
581+
d="M900,656-5.359801043703684 A0,0 0 0,1 90,95.35980104370368"
582582
id="Elric,Mikey"
583583
onClick={[Function]}
584584
onContextMenu={[Function]}
@@ -598,7 +598,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
598598
<g>
599599
<path
600600
className="link"
601-
d="M190-1.8884174089776617,609-5.016108742596914A0,0 0 0,1 31.888417408977663,189.0161087425969"
601+
d="M190-1.8884174089776617,609-5.016108742596914 A0,0 0 0,1 31.888417408977663,189.0161087425969"
602602
id="James,Sarah"
603603
onClick={[Function]}
604604
onContextMenu={[Function]}
@@ -618,7 +618,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
618618
<g>
619619
<path
620620
className="link"
621-
d="M305-2.3591551111937417,745-4.812676426835234A0,0 0 0,1 32.35915511119374,188.81267642683522"
621+
d="M305-2.3591551111937417,745-4.812676426835234 A0,0 0 0,1 32.35915511119374,188.81267642683522"
622622
id="Alice,Sarah"
623623
onClick={[Function]}
624624
onContextMenu={[Function]}
@@ -638,7 +638,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
638638
<g>
639639
<path
640640
className="link"
641-
d="M1904.798325813522697,609-2.388207824995315A0,0 0 0,1 404.2016741864773,502.38820782499533"
641+
d="M1904.798325813522697,609-2.388207824995315 A0,0 0 0,1 404.2016741864773,502.38820782499533"
642642
id="James,Maddy"
643643
onClick={[Function]}
644644
onContextMenu={[Function]}
@@ -658,7 +658,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
658658
<g>
659659
<path
660660
className="link"
661-
d="M120-2.0922556386763027,270-4.934565185557317A0,0 0 0,1 16.092255638676303,24.934565185557318"
661+
d="M120-2.0922556386763027,270-4.934565185557317 A0,0 0 0,1 16.092255638676303,24.934565185557318"
662662
id="Peter,Johan"
663663
onClick={[Function]}
664664
onContextMenu={[Function]}

test/graph/graph.builder.spec.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,12 @@ describe("Graph Helper", () => {
3131
const sourceCoords = { x: 0, y: 0 };
3232
const targetCoords = { x: 0, y: 0 };
3333

34-
expect(linkHelper.buildLinkPathDefinition).toHaveBeenCalledWith(sourceCoords, targetCoords, "STRAIGHT");
34+
expect(linkHelper.buildLinkPathDefinition).toHaveBeenCalledWith(
35+
sourceCoords,
36+
targetCoords,
37+
"STRAIGHT",
38+
undefined
39+
);
3540
});
3641

3742
describe("and no custom color is set", () => {

test/link/link.helper.spec.js

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,50 @@
11
import * as linkHelper from "../../src/components/link/link.helper";
2+
import { LINE_TYPES } from "../../src/components/link/link.const";
23

34
describe("Link Helper", () => {
45
describe("#buildLinkPathDefinition", () => {
5-
test("should return expected path definition", () => {
6-
const sourceCoords = { x: "1", y: "2" };
7-
const targetCoords = { x: "3", y: "4" };
8-
const path = linkHelper.buildLinkPathDefinition(sourceCoords, targetCoords);
6+
describe("when line_type is straight", () => {
7+
test("should return expected straight path definition", () => {
8+
const sourceCoords = { x: 1, y: 2 };
9+
const targetCoords = { x: 3, y: 4 };
10+
const path = linkHelper.buildLinkPathDefinition(sourceCoords, targetCoords, LINE_TYPES.STRAIGHT);
911

10-
expect(path).toEqual("M1,2A0,0 0 0,1 3,4");
12+
expect(path).toEqual("M1,2 A0,0 0 0,1 3,4");
13+
});
14+
});
15+
16+
describe("when line_type is curve-smooth", () => {
17+
test("should return expected curve-smooth path definition", () => {
18+
const sourceCoords = { x: 1, y: 2 };
19+
const targetCoords = { x: 3, y: 4 };
20+
const path = linkHelper.buildLinkPathDefinition(sourceCoords, targetCoords, LINE_TYPES.CURVE_SMOOTH);
21+
22+
expect(path).toEqual("M1,2 A2.8284271247461903,2.8284271247461903 0 0,1 3,4");
23+
});
24+
});
25+
26+
describe("when line_type is curve-full", () => {
27+
test("should return expected curve-full path definition", () => {
28+
const sourceCoords = { x: 1, y: 2 };
29+
const targetCoords = { x: 3, y: 4 };
30+
const path = linkHelper.buildLinkPathDefinition(sourceCoords, targetCoords, LINE_TYPES.CURVE_FULL);
31+
32+
expect(path).toEqual("M1,2 A1,1 0 0,1 3,4");
33+
});
34+
});
35+
36+
describe("when breakPoints specified", () => {
37+
test("should return expected path definition with breakPoints", () => {
38+
const sourceCoords = { x: 1, y: 2 };
39+
const targetCoords = { x: 3, y: 4 };
40+
const breakPoints = [
41+
{ x: 5, y: 6 },
42+
{ x: 7, y: 8 },
43+
];
44+
const path = linkHelper.buildLinkPathDefinition(sourceCoords, targetCoords, LINE_TYPES.STRAIGHT, breakPoints);
45+
46+
expect(path).toEqual("M1,2 A0,0 0 0,1 5,6 A0,0 0 0,1 7,8 A0,0 0 0,1 3,4");
47+
});
1148
});
1249
});
1350
});

0 commit comments

Comments
 (0)