@datarailsshared/dr_renderer 1.2.453 → 1.2.454

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,739 @@
1
+ import { DrChartTooltip, DR_TOOLTIP_OPTIONS_DEFAULT } from "../src/dr_chart_tooltip";
2
+ const helpers = require("../src/dr-renderer-helpers");
3
+
4
+ describe("DrChartTooltip", () => {
5
+ let chartMock;
6
+ let instance;
7
+ let anchorBBoxMock;
8
+ let anchorMock;
9
+ let positionMock;
10
+ let optionsMock;
11
+ let tooltipBBoxMock;
12
+ let tooltipMock;
13
+ let arrowElMock;
14
+ let chartBBoxMock;
15
+
16
+ beforeEach(() => {
17
+ chartBBoxMock = {
18
+ x: 0,
19
+ y: 0,
20
+ width: 800,
21
+ height: 600,
22
+ };
23
+ chartMock = {
24
+ renderer: {
25
+ text: jest.fn().mockImplementation((content) => ({
26
+ attr: jest.fn().mockReturnThis(),
27
+ css: jest.fn().mockReturnThis(),
28
+ add: jest.fn().mockReturnThis(),
29
+ hide: jest.fn().mockReturnThis(),
30
+ getBBox: jest.fn().mockReturnValue({ width: 100, height: 50 }),
31
+ })),
32
+ },
33
+ container: {
34
+ getBoundingClientRect: jest.fn().mockReturnValue(chartBBoxMock),
35
+ },
36
+ chartWidth: 800,
37
+ chartHeight: 600,
38
+ };
39
+
40
+ instance = new DrChartTooltip(chartMock, {
41
+ fontSize: 12,
42
+ fontFamily: "Arial",
43
+ color: "#333",
44
+ });
45
+
46
+ anchorBBoxMock = { x: 100, y: 100, width: 50, height: 30 };
47
+ anchorMock = {
48
+ getBBox: jest.fn().mockReturnValue(anchorBBoxMock),
49
+ };
50
+ positionMock = { x: 60, y: 30, direction: "top" };
51
+ optionsMock = {
52
+ anchorOffset: 10,
53
+ arrowRadius: 3,
54
+ arrowSize: 10,
55
+ background: "#fff",
56
+ border: 1,
57
+ borderColor: "#DFE0E3",
58
+ borderRadius: 8,
59
+ boxShadow: "0px 4px 8px 1px rgba(0, 0, 0, 0.25)",
60
+ direction: "top",
61
+ followPointer: false,
62
+ outerOffset: 8,
63
+ paddings: [6, 12],
64
+ showArrow: true,
65
+ };
66
+ arrowElMock = {
67
+ setAttribute: jest.fn(),
68
+ removeAttribute: jest.fn(),
69
+ };
70
+ tooltipBBoxMock = { width: 40, height: 50 };
71
+ tooltipMock = {
72
+ attr: jest.fn(),
73
+ getBBox: jest.fn().mockReturnValue(tooltipBBoxMock),
74
+ element: {
75
+ querySelector: jest.fn().mockReturnValue(arrowElMock),
76
+ },
77
+ };
78
+
79
+ jest.spyOn(helpers, "clamp");
80
+ jest.spyOn(helpers, "removeSVGTextCorrection");
81
+ });
82
+
83
+ afterEach(() => {
84
+ jest.clearAllMocks();
85
+ });
86
+
87
+ it("initializes with default options", () => {
88
+ const tooltip = new DrChartTooltip(chartMock);
89
+ expect(tooltip.getOptions()).toEqual({ ...DR_TOOLTIP_OPTIONS_DEFAULT });
90
+ });
91
+
92
+ it("initializes with merged options", () => {
93
+ expect(instance).toBeInstanceOf(DrChartTooltip);
94
+ expect(instance.getOptions()).toEqual({
95
+ ...DR_TOOLTIP_OPTIONS_DEFAULT,
96
+ fontSize: 12,
97
+ fontFamily: "Arial",
98
+ color: "#333",
99
+ });
100
+ });
101
+
102
+ describe("add", () => {
103
+ let localOptions;
104
+
105
+ beforeEach(() => {
106
+ localOptions = instance.getOptions();
107
+ });
108
+
109
+ it("return false unless content or anchor are provided", () => {
110
+ expect(instance.add()).toBe(false);
111
+ });
112
+
113
+ it("creates and show a tooltip on element mouseover", () => {
114
+ const anchor = document.createElement("div");
115
+ const content = "Tooltip Content";
116
+
117
+ const destroyMock = jest.fn();
118
+ const showMock = jest.fn();
119
+ instance.create = jest.fn().mockReturnValue({
120
+ destroy: destroyMock,
121
+ show: showMock,
122
+ });
123
+
124
+ instance.add(content, anchor);
125
+
126
+ anchor.dispatchEvent(new MouseEvent("mouseover", {}));
127
+ expect(instance.create).toHaveBeenCalledWith("Tooltip Content", anchor, localOptions);
128
+ expect(showMock).toHaveBeenCalled();
129
+
130
+ // destroy the previous tooltip
131
+ anchor.dispatchEvent(new MouseEvent("mouseover", {}));
132
+ expect(destroyMock).toHaveBeenCalled();
133
+ });
134
+
135
+ it("creates and show a tooltip on point mouseover", () => {
136
+ const anchor = document.createElement("div");
137
+ const content = "Tooltip Content";
138
+
139
+ const showMock = jest.fn();
140
+ instance.create = jest.fn().mockReturnValue({
141
+ show: showMock,
142
+ });
143
+
144
+ instance.add(content, anchor, { followPointer: true });
145
+ const event = new MouseEvent("mouseover", {});
146
+
147
+ anchor.dispatchEvent(event);
148
+ expect(instance.create).toHaveBeenCalledWith("Tooltip Content", event, {
149
+ ...localOptions,
150
+ ...{ followPointer: true },
151
+ });
152
+ expect(showMock).toHaveBeenCalled();
153
+ });
154
+
155
+ it("ignore mouseleave event unless tooltip is created", () => {
156
+ const anchor = document.createElement("div");
157
+ const content = "Tooltip Content";
158
+
159
+ const destroyMock = jest.fn();
160
+ const showMock = jest.fn();
161
+ instance.create = jest.fn().mockReturnValue({
162
+ destroy: destroyMock,
163
+ show: showMock,
164
+ });
165
+
166
+ instance.add(content, anchor, { followPointer: true });
167
+
168
+ anchor.dispatchEvent(new MouseEvent("mouseleave", {}));
169
+ expect(destroyMock).not.toHaveBeenCalled();
170
+ });
171
+
172
+ it("destroys tooltip on mouseleave", () => {
173
+ const anchor = document.createElement("div");
174
+ const content = "Tooltip Content";
175
+
176
+ const destroyMock = jest.fn();
177
+ const showMock = jest.fn();
178
+ instance.create = jest.fn().mockReturnValue({
179
+ destroy: destroyMock,
180
+ show: showMock,
181
+ });
182
+
183
+ instance.add(content, anchor, { followPointer: true });
184
+
185
+ anchor.dispatchEvent(new MouseEvent("mouseover", {}));
186
+ anchor.dispatchEvent(new MouseEvent("mouseleave", {}));
187
+ expect(destroyMock).toHaveBeenCalled();
188
+ });
189
+
190
+ it("updates tooltip position on mousemove", () => {
191
+ const anchor = document.createElement("div");
192
+ const content = "Tooltip Content";
193
+
194
+ const showMock = jest.fn();
195
+ instance.create = jest.fn().mockReturnValue({
196
+ show: showMock,
197
+ });
198
+ instance.setTooltipPosition = jest.fn();
199
+
200
+ instance.add(content, anchor, { followPointer: true });
201
+ const event = new MouseEvent("mousemove", {});
202
+
203
+ anchor.dispatchEvent(event);
204
+ // not call untill tooltip is created
205
+ expect(instance.setTooltipPosition).not.toHaveBeenCalled();
206
+ anchor.dispatchEvent(new MouseEvent("mouseover", {}));
207
+ anchor.dispatchEvent(event);
208
+ // update position for the existing tooltip
209
+ expect(instance.setTooltipPosition).toHaveBeenCalled();
210
+ });
211
+ });
212
+
213
+ describe("create", () => {
214
+ it("creates a tooltip with correct attributes and styles", () => {
215
+ const content = "Tooltip Content";
216
+ instance.setTooltipPosition = jest.fn();
217
+ const options = instance.getOptions();
218
+ const tooltip = instance.create(content, null, options);
219
+
220
+ expect(chartMock.renderer.text).toHaveBeenCalledWith(expect.stringContaining(content), 0, 0, true);
221
+ expect(tooltip.attr).toHaveBeenCalledWith({ class: "dr-renderer-tooltip" });
222
+ expect(tooltip.css).toHaveBeenCalledWith(
223
+ expect.objectContaining({
224
+ background: options.background,
225
+ fontFamily: "Arial",
226
+ fontSize: "12px",
227
+ color: "#333",
228
+ border: `${options.border}px solid ${options.borderColor}`,
229
+ borderRadius: options.borderRadius + "px",
230
+ boxShadow: options.boxShadow,
231
+ color: options.color,
232
+ display: "flex",
233
+ alignItems: "center",
234
+ gap: "4px",
235
+ fontFamily: options.fontFamily,
236
+ fontSize: options.fontSize + "px",
237
+ padding: options.paddings.map((p) => `${p}px`).join(" "),
238
+ })
239
+ );
240
+ });
241
+
242
+ it("sets a tooltip position", () => {
243
+ const options = instance.getOptions();
244
+ instance.setTooltipPosition = jest.fn();
245
+ instance.create("Tooltip content", null, options);
246
+ expect(instance.setTooltipPosition).toHaveBeenCalled();
247
+ });
248
+
249
+ it("adds an arrow if { showArrow: true }", () => {
250
+ const options = instance.getOptions();
251
+ instance.setTooltipPosition = jest.fn();
252
+ instance.create("Tooltip content", null, options);
253
+ expect(chartMock.renderer.text).toHaveBeenCalledWith(
254
+ 'Tooltip content<span class="dr-renderer-tooltip_arrow" style="position: absolute"></span>',
255
+ 0,
256
+ 0,
257
+ true
258
+ );
259
+ });
260
+
261
+ it("does not add an arrow if { showArrow: false }", () => {
262
+ const options = instance.getOptions();
263
+ instance.setTooltipPosition = jest.fn();
264
+ instance.create("Tooltip content", null, { ...options, ...{ showArrow: false } });
265
+ expect(chartMock.renderer.text).toHaveBeenCalledWith("Tooltip content", 0, 0, true);
266
+ });
267
+ });
268
+
269
+ describe("setTooltipPosition", () => {
270
+ beforeEach(() => {
271
+ instance.getPosition = jest.fn().mockReturnValue(positionMock);
272
+ instance.setArrowPosition = jest.fn();
273
+ });
274
+
275
+ it("positions the tooltip correctly", () => {
276
+ instance.setTooltipPosition(tooltipMock, anchorMock, optionsMock);
277
+
278
+ expect(tooltipMock.attr).toHaveBeenCalledWith({
279
+ x: positionMock.x,
280
+ y: positionMock.y,
281
+ class: expect.stringContaining(positionMock.direction),
282
+ });
283
+ });
284
+
285
+ it("positions the tooltip arrow correctly", () => {
286
+ instance.setTooltipPosition(tooltipMock, anchorMock, optionsMock);
287
+ expect(instance.setArrowPosition).toHaveBeenCalledWith(tooltipMock, positionMock, anchorMock, optionsMock);
288
+ });
289
+
290
+ it("does not position the tooltip arrow unless the arrows is on", () => {
291
+ optionsMock.showArrow = false;
292
+ instance.setTooltipPosition(tooltipMock, anchorMock, optionsMock);
293
+ expect(instance.setArrowPosition).not.toHaveBeenCalled();
294
+ });
295
+ });
296
+
297
+ describe("setArrowPosition", () => {
298
+ let commonStyles;
299
+ let arrowBottomBorderStyles;
300
+ let arrowTopBorderStyles;
301
+ let arrowLeftBorderStyles;
302
+ let arrowRightBorderStyles;
303
+
304
+ beforeEach(() => {
305
+ commonStyles = `position:absolute;display:block;width:${optionsMock.arrowSize}px;height:${optionsMock.arrowSize}px;transform:rotate(-45deg);border-style:solid;border-width:${optionsMock.border}px;background:${optionsMock.background};`;
306
+ arrowBottomBorderStyles = `border-color:${optionsMock.borderColor} ${
307
+ optionsMock.borderColor
308
+ } transparent transparent;border-top-right-radius:${optionsMock.arrowRadius}px;top:-${optionsMock.arrowSize / 2}px;`;
309
+ arrowTopBorderStyles = `border-color:transparent transparent ${optionsMock.borderColor} ${
310
+ optionsMock.borderColor
311
+ };border-bottom-left-radius:${optionsMock.arrowRadius}px;bottom:-${optionsMock.arrowSize / 2}px;`;
312
+ arrowLeftBorderStyles = `border-color:transparent ${optionsMock.borderColor} ${
313
+ optionsMock.borderColor
314
+ } transparent;border-bottom-right-radius:${optionsMock.arrowRadius}px;right:-${optionsMock.arrowSize / 2}px;`;
315
+ arrowRightBorderStyles = `border-color:${optionsMock.borderColor} transparent transparent ${
316
+ optionsMock.borderColor
317
+ };border-top-left-radius:${optionsMock.arrowRadius}px;left:-${optionsMock.arrowSize / 2}px;`;
318
+ });
319
+
320
+ it("sets a top arrow position (tooltip is bigger than anchor)", () => {
321
+ tooltipBBoxMock.width = 100;
322
+ tooltipBBoxMock.height = 20;
323
+ anchorBBoxMock.width = 50;
324
+ anchorBBoxMock.height = 50;
325
+ anchorBBoxMock.x = 200;
326
+ anchorBBoxMock.y = 200;
327
+ positionMock = {
328
+ x: 175,
329
+ y: 180,
330
+ direction: "top",
331
+ };
332
+ instance.setArrowPosition(tooltipMock, positionMock, anchorMock, optionsMock);
333
+ expect(arrowElMock.setAttribute).toHaveBeenCalledWith("style", `${commonStyles}${arrowTopBorderStyles}left:45px`);
334
+ });
335
+
336
+ it("sets a top arrow position (tooltip is smaller than anchor)", () => {
337
+ tooltipBBoxMock.width = 40;
338
+ tooltipBBoxMock.height = 20;
339
+ anchorBBoxMock.width = 50;
340
+ anchorBBoxMock.height = 50;
341
+ anchorBBoxMock.x = 200;
342
+ anchorBBoxMock.y = 200;
343
+ positionMock = {
344
+ x: 205,
345
+ y: 180,
346
+ direction: "top",
347
+ };
348
+ instance.setArrowPosition(tooltipMock, positionMock, anchorMock, optionsMock);
349
+ expect(arrowElMock.setAttribute).toHaveBeenCalledWith("style", `${commonStyles}${arrowTopBorderStyles}left:15px`);
350
+ });
351
+
352
+ it("sets a top arrow position (clamp left)", () => {
353
+ tooltipBBoxMock.width = 100;
354
+ tooltipBBoxMock.height = 20;
355
+ anchorBBoxMock.width = 20;
356
+ anchorBBoxMock.height = 20;
357
+ anchorBBoxMock.x = 0;
358
+ anchorBBoxMock.y = 200;
359
+ positionMock = {
360
+ x: 0,
361
+ y: 180,
362
+ direction: "top",
363
+ };
364
+ instance.setArrowPosition(tooltipMock, positionMock, anchorMock, optionsMock);
365
+ expect(arrowElMock.setAttribute).toHaveBeenCalledWith("style", `${commonStyles}${arrowTopBorderStyles}left:8px`);
366
+ });
367
+
368
+ it("sets a top arrow position (clamp right)", () => {
369
+ tooltipBBoxMock.width = 100;
370
+ tooltipBBoxMock.height = 20;
371
+ anchorBBoxMock.width = 20;
372
+ anchorBBoxMock.height = 20;
373
+ anchorBBoxMock.x = 180;
374
+ anchorBBoxMock.y = 200;
375
+ positionMock = {
376
+ x: 100,
377
+ y: 180,
378
+ direction: "top",
379
+ };
380
+ instance.setArrowPosition(tooltipMock, positionMock, anchorMock, optionsMock);
381
+ expect(arrowElMock.setAttribute).toHaveBeenCalledWith("style", `${commonStyles}${arrowTopBorderStyles}left:82px`);
382
+ });
383
+
384
+ it("sets a bottom arrow position (tooltip is bigger than anchor)", () => {
385
+ tooltipBBoxMock.width = 100;
386
+ tooltipBBoxMock.height = 20;
387
+ anchorBBoxMock.width = 50;
388
+ anchorBBoxMock.height = 50;
389
+ anchorBBoxMock.x = 200;
390
+ anchorBBoxMock.y = 200;
391
+ positionMock = {
392
+ x: 175,
393
+ y: 180,
394
+ direction: "bottom",
395
+ };
396
+ instance.setArrowPosition(tooltipMock, positionMock, anchorMock, optionsMock);
397
+ expect(arrowElMock.setAttribute).toHaveBeenCalledWith("style", `${commonStyles}${arrowBottomBorderStyles}left:45px`);
398
+ });
399
+
400
+ it("sets a bottom arrow position (tooltip is smaller than anchor)", () => {
401
+ tooltipBBoxMock.width = 40;
402
+ tooltipBBoxMock.height = 20;
403
+ anchorBBoxMock.width = 50;
404
+ anchorBBoxMock.height = 50;
405
+ anchorBBoxMock.x = 200;
406
+ anchorBBoxMock.y = 200;
407
+ positionMock = {
408
+ x: 205,
409
+ y: 180,
410
+ direction: "bottom",
411
+ };
412
+ instance.setArrowPosition(tooltipMock, positionMock, anchorMock, optionsMock);
413
+ expect(arrowElMock.setAttribute).toHaveBeenCalledWith("style", `${commonStyles}${arrowBottomBorderStyles}left:15px`);
414
+ });
415
+
416
+ it("sets a bottom arrow position (clamp left)", () => {
417
+ tooltipBBoxMock.width = 100;
418
+ tooltipBBoxMock.height = 20;
419
+ anchorBBoxMock.width = 20;
420
+ anchorBBoxMock.height = 20;
421
+ anchorBBoxMock.x = 0;
422
+ anchorBBoxMock.y = 200;
423
+ positionMock = {
424
+ x: 0,
425
+ y: 180,
426
+ direction: "bottom",
427
+ };
428
+ instance.setArrowPosition(tooltipMock, positionMock, anchorMock, optionsMock);
429
+ expect(arrowElMock.setAttribute).toHaveBeenCalledWith("style", `${commonStyles}${arrowBottomBorderStyles}left:8px`);
430
+ });
431
+
432
+ it("sets a bottom arrow position (clamp right)", () => {
433
+ tooltipBBoxMock.width = 100;
434
+ tooltipBBoxMock.height = 20;
435
+ anchorBBoxMock.width = 20;
436
+ anchorBBoxMock.height = 20;
437
+ anchorBBoxMock.x = 180;
438
+ anchorBBoxMock.y = 200;
439
+ positionMock = {
440
+ x: 100,
441
+ y: 180,
442
+ direction: "bottom",
443
+ };
444
+ instance.setArrowPosition(tooltipMock, positionMock, anchorMock, optionsMock);
445
+ expect(arrowElMock.setAttribute).toHaveBeenCalledWith("style", `${commonStyles}${arrowBottomBorderStyles}left:82px`);
446
+ });
447
+
448
+ it("sets a left arrow position (tooltip is bigger than anchor)", () => {
449
+ tooltipBBoxMock.width = 20;
450
+ tooltipBBoxMock.height = 100;
451
+ anchorBBoxMock.width = 50;
452
+ anchorBBoxMock.height = 50;
453
+ anchorBBoxMock.x = 200;
454
+ anchorBBoxMock.y = 200;
455
+ positionMock = {
456
+ x: 180,
457
+ y: 175,
458
+ direction: "left",
459
+ };
460
+ instance.setArrowPosition(tooltipMock, positionMock, anchorMock, optionsMock);
461
+ expect(arrowElMock.setAttribute).toHaveBeenCalledWith("style", `${commonStyles}${arrowLeftBorderStyles}top:45px`);
462
+ });
463
+
464
+ it("sets a left arrow position (tooltip is smaller than anchor)", () => {
465
+ tooltipBBoxMock.width = 20;
466
+ tooltipBBoxMock.height = 40;
467
+ anchorBBoxMock.width = 50;
468
+ anchorBBoxMock.height = 50;
469
+ anchorBBoxMock.x = 200;
470
+ anchorBBoxMock.y = 200;
471
+ positionMock = {
472
+ x: 180,
473
+ y: 205,
474
+ direction: "left",
475
+ };
476
+ instance.setArrowPosition(tooltipMock, positionMock, anchorMock, optionsMock);
477
+ expect(arrowElMock.setAttribute).toHaveBeenCalledWith("style", `${commonStyles}${arrowLeftBorderStyles}top:15px`);
478
+ });
479
+
480
+ it("sets a left arrow position (clamp top)", () => {
481
+ tooltipBBoxMock.width = 20;
482
+ tooltipBBoxMock.height = 100;
483
+ anchorBBoxMock.width = 20;
484
+ anchorBBoxMock.height = 20;
485
+ anchorBBoxMock.x = 200;
486
+ anchorBBoxMock.y = 0;
487
+ positionMock = {
488
+ x: 180,
489
+ y: 0,
490
+ direction: "left",
491
+ };
492
+ instance.setArrowPosition(tooltipMock, positionMock, anchorMock, optionsMock);
493
+ expect(arrowElMock.setAttribute).toHaveBeenCalledWith("style", `${commonStyles}${arrowLeftBorderStyles}top:8px`);
494
+ });
495
+
496
+ it("sets a left arrow position (clamp bottom)", () => {
497
+ tooltipBBoxMock.width = 20;
498
+ tooltipBBoxMock.height = 100;
499
+ anchorBBoxMock.width = 20;
500
+ anchorBBoxMock.height = 20;
501
+ anchorBBoxMock.x = 200;
502
+ anchorBBoxMock.y = 180;
503
+ positionMock = {
504
+ x: 180,
505
+ y: 10,
506
+ direction: "left",
507
+ };
508
+ instance.setArrowPosition(tooltipMock, positionMock, anchorMock, optionsMock);
509
+ expect(arrowElMock.setAttribute).toHaveBeenCalledWith("style", `${commonStyles}${arrowLeftBorderStyles}top:82px`);
510
+ });
511
+
512
+ it("sets a right arrow position (tooltip is bigger than anchor)", () => {
513
+ tooltipBBoxMock.width = 20;
514
+ tooltipBBoxMock.height = 100;
515
+ anchorBBoxMock.width = 50;
516
+ anchorBBoxMock.height = 50;
517
+ anchorBBoxMock.x = 200;
518
+ anchorBBoxMock.y = 200;
519
+ positionMock = {
520
+ x: 180,
521
+ y: 175,
522
+ direction: "right",
523
+ };
524
+ instance.setArrowPosition(tooltipMock, positionMock, anchorMock, optionsMock);
525
+ expect(arrowElMock.setAttribute).toHaveBeenCalledWith("style", `${commonStyles}${arrowRightBorderStyles}top:45px`);
526
+ });
527
+
528
+ it("sets a right arrow position (tooltip is smaller than anchor)", () => {
529
+ tooltipBBoxMock.width = 20;
530
+ tooltipBBoxMock.height = 40;
531
+ anchorBBoxMock.width = 50;
532
+ anchorBBoxMock.height = 50;
533
+ anchorBBoxMock.x = 200;
534
+ anchorBBoxMock.y = 200;
535
+ positionMock = {
536
+ x: 180,
537
+ y: 205,
538
+ direction: "right",
539
+ };
540
+ instance.setArrowPosition(tooltipMock, positionMock, anchorMock, optionsMock);
541
+ expect(arrowElMock.setAttribute).toHaveBeenCalledWith("style", `${commonStyles}${arrowRightBorderStyles}top:15px`);
542
+ });
543
+
544
+ it("sets a right arrow position (clamp top)", () => {
545
+ tooltipBBoxMock.width = 20;
546
+ tooltipBBoxMock.height = 100;
547
+ anchorBBoxMock.width = 20;
548
+ anchorBBoxMock.height = 20;
549
+ anchorBBoxMock.x = 200;
550
+ anchorBBoxMock.y = 0;
551
+ positionMock = {
552
+ x: 180,
553
+ y: 0,
554
+ direction: "right",
555
+ };
556
+ instance.setArrowPosition(tooltipMock, positionMock, anchorMock, optionsMock);
557
+ expect(arrowElMock.setAttribute).toHaveBeenCalledWith("style", `${commonStyles}${arrowRightBorderStyles}top:8px`);
558
+ });
559
+
560
+ it("sets a right arrow position (clamp bottom)", () => {
561
+ tooltipBBoxMock.width = 20;
562
+ tooltipBBoxMock.height = 100;
563
+ anchorBBoxMock.width = 20;
564
+ anchorBBoxMock.height = 20;
565
+ anchorBBoxMock.x = 200;
566
+ anchorBBoxMock.y = 180;
567
+ positionMock = {
568
+ x: 180,
569
+ y: 10,
570
+ direction: "right",
571
+ };
572
+ instance.setArrowPosition(tooltipMock, positionMock, anchorMock, optionsMock);
573
+ expect(arrowElMock.setAttribute).toHaveBeenCalledWith("style", `${commonStyles}${arrowRightBorderStyles}top:82px`);
574
+ });
575
+
576
+ it("removes old styles", () => {
577
+ instance.setArrowPosition(tooltipMock, positionMock, anchorMock, optionsMock);
578
+ expect(arrowElMock.removeAttribute).toHaveBeenCalledWith("style");
579
+ });
580
+ });
581
+
582
+ describe("getCoords", () => {
583
+ beforeEach(() => {
584
+ tooltipBBoxMock.width = 100;
585
+ tooltipBBoxMock.height = 20;
586
+ anchorBBoxMock.width = 50;
587
+ anchorBBoxMock.height = 50;
588
+ anchorBBoxMock.x = 200;
589
+ anchorBBoxMock.y = 200;
590
+ });
591
+
592
+ it("gets coordinates (top - without correction)", () => {
593
+ expect(instance.getCoords("top", tooltipMock, anchorMock, optionsMock)).toEqual({ x: 175, y: 170 });
594
+ });
595
+
596
+ it("gets coordinates (top - bound to the chart - left)", () => {
597
+ anchorBBoxMock.x = 0;
598
+ expect(instance.getCoords("top", tooltipMock, anchorMock, optionsMock)).toEqual({ x: 8 /* offset */, y: 170 });
599
+ });
600
+
601
+ it("gets coordinates (top - bound to the chart - right)", () => {
602
+ anchorBBoxMock.x = 1000;
603
+ expect(instance.getCoords("top", tooltipMock, anchorMock, optionsMock)).toEqual({
604
+ x: 692 /* chartWidth - tooltipWidth - offset */,
605
+ y: 170,
606
+ });
607
+ });
608
+
609
+ it("gets coordinates (bottom - without correction)", () => {
610
+ expect(instance.getCoords("bottom", tooltipMock, anchorMock, optionsMock)).toEqual({ x: 175, y: 260 });
611
+ });
612
+
613
+ it("gets coordinates (bottom - bound to the chart - left)", () => {
614
+ anchorBBoxMock.x = 0;
615
+ expect(instance.getCoords("bottom", tooltipMock, anchorMock, optionsMock)).toEqual({ x: 8 /* offset */, y: 260 });
616
+ });
617
+
618
+ it("gets coordinates (bottom - bound to the chart - right)", () => {
619
+ anchorBBoxMock.x = 1000;
620
+ expect(instance.getCoords("bottom", tooltipMock, anchorMock, optionsMock)).toEqual({
621
+ x: 692 /* chartWidth - tooltipWidth - offset */,
622
+ y: 260,
623
+ });
624
+ });
625
+
626
+ it("gets coordinates (left - without correction)", () => {
627
+ expect(instance.getCoords("left", tooltipMock, anchorMock, optionsMock)).toEqual({ x: 90, y: 215 });
628
+ });
629
+
630
+ it("gets coordinates (left - bound to the chart - bottom)", () => {
631
+ anchorBBoxMock.y = 1000;
632
+ expect(instance.getCoords("left", tooltipMock, anchorMock, optionsMock)).toEqual({ x: 90, y: 572 });
633
+ });
634
+
635
+ it("gets coordinates (left - bound to the chart - top)", () => {
636
+ anchorBBoxMock.y = 0;
637
+ tooltipBBoxMock.height = 100;
638
+ expect(instance.getCoords("left", tooltipMock, anchorMock, optionsMock)).toEqual({
639
+ x: 90,
640
+ y: 8,
641
+ });
642
+ });
643
+
644
+ it("gets coordinates (right - without correction)", () => {
645
+ expect(instance.getCoords("right", tooltipMock, anchorMock, optionsMock)).toEqual({ x: 260, y: 215 });
646
+ });
647
+
648
+ it("gets coordinates (right - bound to the chart - bottom)", () => {
649
+ anchorBBoxMock.y = 1000;
650
+ expect(instance.getCoords("right", tooltipMock, anchorMock, optionsMock)).toEqual({
651
+ x: 260,
652
+ y: 572,
653
+ });
654
+ });
655
+
656
+ it("gets coordinates (right - bound to the chart - top)", () => {
657
+ anchorBBoxMock.y = 0;
658
+ tooltipBBoxMock.height = 100;
659
+ expect(instance.getCoords("right", tooltipMock, anchorMock, optionsMock)).toEqual({
660
+ x: 260,
661
+ y: 8,
662
+ });
663
+ });
664
+ });
665
+
666
+ describe("getAnchorBox", () => {
667
+ beforeEach(() => {
668
+ chartBBoxMock.x = 100;
669
+ chartBBoxMock.y = 100;
670
+ });
671
+
672
+ it("returns the element bbox (svg element)", () => {
673
+ expect(
674
+ instance.getAnchorBox({
675
+ getBBox: jest.fn().mockReturnValue({ x: 10, y: 10, width: 100, height: 20 }),
676
+ })
677
+ ).toEqual({ x: 10, y: 10, width: 100, height: 20 });
678
+ });
679
+
680
+ it("returns the element bbox (html element), uses chart x and y to get reletive coordinates", () => {
681
+ expect(
682
+ instance.getAnchorBox({
683
+ getBoundingClientRect: jest.fn().mockReturnValue({ x: 150, y: 160, width: 100, height: 20 }),
684
+ })
685
+ ).toEqual({ x: 50, y: 60, width: 100, height: 20 });
686
+ });
687
+
688
+ it("returns the element bbox (point), uses chart x and y to get reletive coordinates", () => {
689
+ expect(instance.getAnchorBox({ x: 150, y: 160 })).toEqual({ x: 50, y: 60, width: 0, height: 0 });
690
+ });
691
+ });
692
+
693
+ describe("getPosition", () => {
694
+ it("returns coords and direction for provided direction if it's valid", () => {
695
+ optionsMock.direction = "left";
696
+ instance.getCoords = jest.fn().mockReturnValue({ x: 100, y: 100 });
697
+ expect(instance.getPosition(tooltipMock, anchorMock, optionsMock)).toEqual({
698
+ x: 100,
699
+ y: 100,
700
+ direction: "left",
701
+ });
702
+ });
703
+
704
+ it("looks for an appropriate directions unless it's valid", () => {
705
+ optionsMock.direction = "top";
706
+ instance.getCoords = jest.fn((direction) => {
707
+ // try top -> invalid, out of y
708
+ if (direction === "top") {
709
+ return {
710
+ x: 100,
711
+ y: -20,
712
+ };
713
+ // try right, invalid -> out of x
714
+ } else if (direction === "right") {
715
+ return {
716
+ x: -100,
717
+ y: 20,
718
+ };
719
+ // try bottom, invalid -> out of y
720
+ } else if (direction === "bottom") {
721
+ return {
722
+ x: 100,
723
+ y: 2000,
724
+ };
725
+ } else {
726
+ return {
727
+ x: 50,
728
+ y: 50,
729
+ };
730
+ }
731
+ });
732
+ expect(instance.getPosition(tooltipMock, anchorMock, optionsMock)).toEqual({
733
+ x: 50,
734
+ y: 50,
735
+ direction: "left",
736
+ });
737
+ });
738
+ });
739
+ });