Skip to content

Commit 2e0cca1

Browse files
authored
Merge pull request #1683 from plotly/annotation-text-position-fix
Drawing bbox fix for window scroll
2 parents a86a5ac + 66259ed commit 2e0cca1

File tree

9 files changed

+120
-39
lines changed

9 files changed

+120
-39
lines changed

src/components/drawing/index.js

+18-20
Original file line numberDiff line numberDiff line change
@@ -557,11 +557,11 @@ drawing.steps = function(shape) {
557557
};
558558

559559
// off-screen svg render testing element, shared by the whole page
560-
// uses the id 'js-plotly-tester' and stores it in gd._tester
560+
// uses the id 'js-plotly-tester' and stores it in drawing.tester
561561
// makes a hash of cached text items in tester.node()._cache
562562
// so we can add references to rendered text (including all info
563563
// needed to fully determine its bounding rect)
564-
drawing.makeTester = function(gd) {
564+
drawing.makeTester = function() {
565565
var tester = d3.select('body')
566566
.selectAll('#js-plotly-tester')
567567
.data([0]);
@@ -594,16 +594,16 @@ drawing.makeTester = function(gd) {
594594
tester.node()._cache = {};
595595
}
596596

597-
gd._tester = tester;
598-
gd._testref = testref;
597+
drawing.tester = tester;
598+
drawing.testref = testref;
599599
};
600600

601601
// use our offscreen tester to get a clientRect for an element,
602602
// in a reference frame where it isn't translated and its anchor
603603
// point is at (0,0)
604604
// always returns a copy of the bbox, so the caller can modify it safely
605-
var savedBBoxes = [],
606-
maxSavedBBoxes = 10000;
605+
var savedBBoxes = [];
606+
var maxSavedBBoxes = 10000;
607607

608608
drawing.bBox = function(node) {
609609
// cache elements we've already measured so we don't have to
@@ -613,14 +613,13 @@ drawing.bBox = function(node) {
613613
return Lib.extendFlat({}, savedBBoxes[saveNum.value]);
614614
}
615615

616-
if(!drawing.test3) {
617-
drawing.test3 = d3.select('#js-plotly-tester');
618-
drawing.tester = drawing.test3.node();
619-
}
616+
var tester3 = drawing.tester;
617+
var tester = tester3.node();
620618

621619
// copy the node to test into the tester
622620
var testNode = node.cloneNode(true);
623-
drawing.tester.appendChild(testNode);
621+
tester.appendChild(testNode);
622+
624623
// standardize its position... do we really want to do this?
625624
d3.select(testNode).attr({
626625
x: 0,
@@ -629,20 +628,19 @@ drawing.bBox = function(node) {
629628
});
630629

631630
var testRect = testNode.getBoundingClientRect();
632-
if(!drawing.refRect) {
633-
drawing.refRect = drawing.test3.select('.js-reference-point')
634-
.node().getBoundingClientRect();
635-
}
631+
var refRect = drawing.testref
632+
.node()
633+
.getBoundingClientRect();
636634

637-
drawing.tester.removeChild(testNode);
635+
tester.removeChild(testNode);
638636

639637
var bb = {
640638
height: testRect.height,
641639
width: testRect.width,
642-
left: testRect.left - drawing.refRect.left,
643-
top: testRect.top - drawing.refRect.top,
644-
right: testRect.right - drawing.refRect.left,
645-
bottom: testRect.bottom - drawing.refRect.top
640+
left: testRect.left - refRect.left,
641+
top: testRect.top - refRect.top,
642+
right: testRect.right - refRect.left,
643+
bottom: testRect.bottom - refRect.top
646644
};
647645

648646
// make sure we don't have too many saved boxes,

src/components/sliders/draw.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ function keyFunction(opts) {
117117

118118
// Compute the dimensions (mutates sliderOpts):
119119
function findDimensions(gd, sliderOpts) {
120-
var sliderLabels = gd._tester.selectAll('g.' + constants.labelGroupClass)
120+
var sliderLabels = Drawing.tester.selectAll('g.' + constants.labelGroupClass)
121121
.data(sliderOpts.steps);
122122

123123
sliderLabels.enter().append('g')
@@ -154,7 +154,7 @@ function findDimensions(gd, sliderOpts) {
154154

155155
if(sliderOpts.currentvalue.visible) {
156156
// Get the dimensions of the current value label:
157-
var dummyGroup = gd._tester.append('g');
157+
var dummyGroup = Drawing.tester.append('g');
158158

159159
sliderLabels.each(function(stepOpts) {
160160
var curValPrefix = drawCurrentValue(dummyGroup, sliderOpts, stepOpts.label);

src/components/updatemenus/draw.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ function findDimensions(gd, menuOpts) {
504504
menuOpts.lx = 0;
505505
menuOpts.ly = 0;
506506

507-
var fakeButtons = gd._tester.selectAll('g.' + constants.dropdownButtonClassName)
507+
var fakeButtons = Drawing.tester.selectAll('g.' + constants.dropdownButtonClassName)
508508
.data(menuOpts.buttons);
509509

510510
fakeButtons.enter().append('g')

src/lib/svg_text_utils.js

+16-4
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,20 @@ var Lib = require('../lib');
1717
var xmlnsNamespaces = require('../constants/xmlns_namespaces');
1818
var stringMappings = require('../constants/string_mappings');
1919

20-
// Append SVG
20+
var DOM_PARSER;
21+
22+
exports.getDOMParser = function() {
23+
if(DOM_PARSER) {
24+
return DOM_PARSER;
25+
} else if(window.DOMParser) {
26+
DOM_PARSER = new window.DOMParser();
27+
return DOM_PARSER;
28+
} else {
29+
throw new Error('Cannot initialize DOMParser');
30+
}
31+
};
2132

22-
var parser = new DOMParser();
33+
// Append SVG
2334

2435
d3.selection.prototype.appendSVG = function(_svgString) {
2536
var skeleton = [
@@ -29,8 +40,9 @@ d3.selection.prototype.appendSVG = function(_svgString) {
2940
'</svg>'
3041
].join('');
3142

32-
var dom = parser.parseFromString(skeleton, 'application/xml'),
33-
childNode = dom.documentElement.firstChild;
43+
var domParser = exports.getDOMParser();
44+
var dom = domParser.parseFromString(skeleton, 'application/xml');
45+
var childNode = dom.documentElement.firstChild;
3446

3547
while(childNode) {
3648
this.node().appendChild(this.node().ownerDocument.importNode(childNode, true));

src/plot_api/plot_api.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@ Plotly.plot = function(gd, data, layout, config) {
9393
d3.select(gd).classed('js-plotly-plot', true);
9494

9595
// off-screen getBoundingClientRect testing space,
96-
// in #js-plotly-tester (and stored as gd._tester)
96+
// in #js-plotly-tester (and stored as Drawing.tester)
9797
// so we can share cached text across tabs
98-
Drawing.makeTester(gd);
98+
Drawing.makeTester();
9999

100100
// collect promises for any async actions during plotting
101101
// any part of the plotting code can push to gd._promises, then

src/plots/plots.js

-2
Original file line numberDiff line numberDiff line change
@@ -1173,8 +1173,6 @@ plots.purge = function(gd) {
11731173

11741174
// these get recreated on Plotly.plot anyway, but just to be safe
11751175
// (and to have a record of them...)
1176-
delete gd._tester;
1177-
delete gd._testref;
11781176
delete gd._promises;
11791177
delete gd._redrawTimer;
11801178
delete gd.firstscatter;

src/traces/carpet/plot.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ function plotOne(gd, plotinfo, cd) {
5959
drawGridLines(xa, ya, boundaryLayer, aax, 'a-boundary', aax._boundarylines);
6060
drawGridLines(xa, ya, boundaryLayer, bax, 'b-boundary', bax._boundarylines);
6161

62-
var maxAExtent = drawAxisLabels(gd._tester, xa, ya, trace, t, labelLayer, aax._labels, 'a-label');
63-
var maxBExtent = drawAxisLabels(gd._tester, xa, ya, trace, t, labelLayer, bax._labels, 'b-label');
62+
var maxAExtent = drawAxisLabels(Drawing.tester, xa, ya, trace, t, labelLayer, aax._labels, 'a-label');
63+
var maxBExtent = drawAxisLabels(Drawing.tester, xa, ya, trace, t, labelLayer, bax._labels, 'b-label');
6464

6565
drawAxisTitles(labelLayer, trace, t, xa, ya, maxAExtent, maxBExtent);
6666

test/jasmine/tests/drawing_test.js

+79-4
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1-
var Drawing = require('@src/components/drawing');
2-
31
var d3 = require('d3');
4-
52
var Plotly = require('@lib/index');
6-
3+
var Drawing = require('@src/components/drawing');
74
var createGraphDiv = require('../assets/create_graph_div');
85
var destroyGraphDiv = require('../assets/destroy_graph_div');
96
var fail = require('../assets/fail_test');
7+
var customMatchers = require('../assets/custom_matchers');
108

119
describe('Drawing', function() {
1210
'use strict';
1311

12+
beforeAll(function() {
13+
jasmine.addMatchers(customMatchers);
14+
});
15+
1416
describe('setClipUrl', function() {
1517

1618
beforeEach(function() {
@@ -348,6 +350,79 @@ describe('Drawing', function() {
348350
expect(g.attr('transform')).toEqual('translate(8,9) scale(4,5) translate(-8,-9) translate(1, 2)');
349351
});
350352
});
353+
354+
describe('bBox', function() {
355+
afterEach(destroyGraphDiv);
356+
357+
function assertBBox(actual, expected) {
358+
expect(actual.height).toEqual(expected.height, 'height');
359+
expect(actual.top).toEqual(expected.top, 'top');
360+
expect(actual.bottom).toEqual(expected.bottom, 'bottom');
361+
362+
var TOL = 3;
363+
expect(actual.width).toBeWithin(expected.width, TOL, 'width');
364+
expect(actual.left).toBeWithin(expected.left, TOL, 'left');
365+
expect(actual.right).toBeWithin(expected.right, TOL, 'right');
366+
}
367+
368+
it('should update bounding box dimension on window scroll', function(done) {
369+
var gd = createGraphDiv();
370+
371+
// allow page to scroll
372+
gd.style.position = 'static';
373+
374+
Plotly.plot(gd, [{
375+
y: [1, 2, 1]
376+
}], {
377+
annotations: [{
378+
text: 'hello'
379+
}],
380+
height: window.innerHeight * 2,
381+
width: 500
382+
})
383+
.then(function() {
384+
var node = d3.select('text.annotation').node();
385+
assertBBox(Drawing.bBox(node), {
386+
height: 14,
387+
width: 27.671875,
388+
left: -13.671875,
389+
top: -11,
390+
right: 14,
391+
bottom: 3
392+
});
393+
394+
window.scroll(0, 200);
395+
return Plotly.relayout(gd, 'annotations[0].text', 'HELLO');
396+
})
397+
.then(function() {
398+
var node = d3.select('text.annotation').node();
399+
assertBBox(Drawing.bBox(node), {
400+
height: 14,
401+
width: 41.015625,
402+
left: -20.671875,
403+
top: -11,
404+
right: 20.34375,
405+
bottom: 3
406+
});
407+
408+
window.scroll(200, 0);
409+
return Plotly.relayout(gd, 'annotations[0].font.size', 20);
410+
})
411+
.then(function() {
412+
var node = d3.select('text.annotation').node();
413+
assertBBox(Drawing.bBox(node), {
414+
height: 22,
415+
width: 66.015625,
416+
left: -32.78125,
417+
top: -18,
418+
right: 33.234375,
419+
bottom: 4
420+
});
421+
})
422+
.catch(fail)
423+
.then(done);
424+
});
425+
});
351426
});
352427

353428
describe('gradients', function() {

test/jasmine/tests/plots_test.js

-2
Original file line numberDiff line numberDiff line change
@@ -428,8 +428,6 @@ describe('Test Plots', function() {
428428
expect(gd.undonum).toBeUndefined();
429429
expect(gd.autoplay).toBeUndefined();
430430
expect(gd.changed).toBeUndefined();
431-
expect(gd._tester).toBeUndefined();
432-
expect(gd._testref).toBeUndefined();
433431
expect(gd._promises).toBeUndefined();
434432
expect(gd._redrawTimer).toBeUndefined();
435433
expect(gd.firstscatter).toBeUndefined();

0 commit comments

Comments
 (0)