diff --git a/src/snapshot/tosvg.js b/src/snapshot/tosvg.js index 9249a871a45..507bab751fd 100644 --- a/src/snapshot/tosvg.js +++ b/src/snapshot/tosvg.js @@ -88,7 +88,7 @@ module.exports = function toSVG(gd, format, scale) { // fill whatever container it's displayed in regardless of plot size. svg.node().style.background = ''; - svg.selectAll('text,tspan') + svg.selectAll('text') .attr({'data-unformatted': null, 'data-math': null}) .each(function() { var txt = d3.select(this); diff --git a/src/traces/indicator/constants.js b/src/traces/indicator/constants.js index b11d4d3e5bc..695a24bed3b 100644 --- a/src/traces/indicator/constants.js +++ b/src/traces/indicator/constants.js @@ -15,5 +15,6 @@ module.exports = { bulletPadding: 0.025, innerRadius: 0.75, valueThickness: 0.5, // thickness of value bars relative to full thickness, - titlePadding: 5 + titlePadding: 5, + horizontalPadding: 10 }; diff --git a/src/traces/indicator/plot.js b/src/traces/indicator/plot.js index fe8fb1438c9..9621ddf1bbd 100644 --- a/src/traces/indicator/plot.js +++ b/src/traces/indicator/plot.js @@ -74,7 +74,6 @@ module.exports = function plot(gd, cdModule, transitionOpts, makeOnCompleteCallb var centerX = size.l + size.w / 2; var centerY = size.t + size.h / 2; - // Angular gauge size var radius = Math.min(size.w / 2, size.h); // fill domain var innerRadius = cn.innerRadius * radius; @@ -519,39 +518,15 @@ function drawNumbers(gd, plotGroup, cd, opts) { var trace = cd[0].trace; var numbersX = opts.numbersX; var numbersY = opts.numbersY; - var numbersAnchor = anchor[trace.align || 'center']; + var numbersAlign = trace.align || 'center'; + var numbersAnchor = anchor[numbersAlign]; var hasTransition = opts.hasTransition; var transitionOpts = opts.transitionOpts; var onComplete = opts.onComplete; - var bignumberFontSize, deltaFontSize; - if(trace._hasNumber) bignumberFontSize = trace.number.font.size; - if(trace._hasDelta) deltaFontSize = trace.delta.font.size; - - // Position delta relative to bignumber - var deltaDy = 0; - var deltaX = 0; - var bignumberY = 0; - - if(trace._hasDelta && trace._hasNumber) { - if(trace.delta.position === 'bottom') { - deltaDy = deltaFontSize * 1.5; - } - if(trace.delta.position === 'top') { - deltaDy = -bignumberFontSize + MID_SHIFT * deltaFontSize; - } - if(trace.delta.position === 'right') { - deltaX = undefined; - } - if(trace.delta.position === 'left') { - deltaX = undefined; - bignumberY = MID_SHIFT * bignumberFontSize / 2; - } - } - deltaDy -= MID_SHIFT * deltaFontSize; - - var numbers = Lib.ensureSingle(plotGroup, 'text', 'numbers'); + var numbers = Lib.ensureSingle(plotGroup, 'g', 'numbers'); + var bignumberbBox, deltabBox; var data = []; if(trace._hasNumber) data.push('number'); @@ -559,19 +534,15 @@ function drawNumbers(gd, plotGroup, cd, opts) { data.push('delta'); if(trace.delta.position === 'left') data.reverse(); } - var sel = numbers.selectAll('tspan').data(data); - sel.enter().append('tspan'); + var sel = numbers.selectAll('text').data(data); + sel.enter().append('text'); sel .attr('text-anchor', function() {return numbersAnchor;}) .attr('class', function(d) { return d;}) - .attr('dx', function(d, i) { - // Add padding to the second tspan when it's a one-liner - if(i === 1) { - var pos = trace.delta.position; - if(pos === 'left' || pos === 'right') return 10; - } - return null; - }); + .attr('x', null) + .attr('y', null) + .attr('dx', null) + .attr('dy', null); sel.exit().remove(); function drawBignumber() { @@ -581,11 +552,9 @@ function drawNumbers(gd, plotGroup, cd, opts) { var bignumberSuffix = trace.number.suffix; var bignumberPrefix = trace.number.prefix; - var number = numbers.select('tspan.number'); + var number = numbers.select('text.number'); number - .call(Drawing.font, trace.number.font) - .attr('x', null) - .attr('dy', bignumberY); + .call(Drawing.font, trace.number.font); if(hasTransition) { number @@ -604,6 +573,11 @@ function drawNumbers(gd, plotGroup, cd, opts) { } else { number.text(bignumberPrefix + fmt(cd[0].y) + bignumberSuffix); } + + number.attr('data-unformatted', bignumberPrefix + fmt(cd[0].y) + bignumberSuffix); + bignumberbBox = Drawing.bBox(number.node()); + + return number; } function drawDelta() { @@ -622,12 +596,10 @@ function drawNumbers(gd, plotGroup, cd, opts) { var deltaFill = function(d) { return d.delta >= 0 ? trace.delta.increasing.color : trace.delta.decreasing.color; }; - var delta = numbers.select('tspan.delta'); + var delta = numbers.select('text.delta'); delta .call(Drawing.font, trace.delta.font) - .call(Color.fill, deltaFill(cd[0])) - .attr('x', deltaX) - .attr('dy', deltaDy); + .call(Color.fill, deltaFill(cd[0])); if(hasTransition) { delta @@ -650,11 +622,46 @@ function drawNumbers(gd, plotGroup, cd, opts) { return deltaFormatText(deltaValue(cd[0])); }); } + + delta.attr('data-unformatted', deltaFormatText(deltaValue(cd[0]))); + deltabBox = Drawing.bBox(delta.node()); + + return delta; } - if(trace._hasDelta) drawDelta(); + // Position delta relative to bignumber + var delta; + if(trace._hasDelta) delta = drawDelta(); if(trace._hasNumber) drawBignumber(); + if(trace._hasDelta && trace._hasNumber) { + var bignumberCenter = [ + (bignumberbBox.left + bignumberbBox.right) / 2, + (bignumberbBox.top + bignumberbBox.bottom) / 2 + ]; + var deltaCenter = [ + (deltabBox.left + deltabBox.right) / 2, + (deltabBox.top + deltabBox.bottom) / 2 + ]; + + if(trace.delta.position === 'left') { + delta.attr('dx', bignumberbBox.left - deltabBox.right - cn.horizontalPadding); + delta.attr('dy', bignumberCenter[1] - deltaCenter[1]); + } + if(trace.delta.position === 'right') { + delta.attr('dx', bignumberbBox.right - deltabBox.left + cn.horizontalPadding); + delta.attr('dy', bignumberCenter[1] - deltaCenter[1]); + } + if(trace.delta.position === 'bottom') { + delta.attr('dx', null); + delta.attr('dy', deltabBox.height); + } + if(trace.delta.position === 'top') { + delta.attr('dx', null); + delta.attr('dy', bignumberbBox.top); + } + } + // Resize numbers to fit within space and position numbers.attr('transform', function() { var m = opts.numbersScaler(numbers); @@ -677,7 +684,10 @@ function drawNumbers(gd, plotGroup, cd, opts) { // Stash the top position of numbersbBox for title positioning trace._numbersTop = scaleRatio * (numbersbBox.top) + translateY; - return strTranslate(numbersX, translateY) + ' scale(' + scaleRatio + ')'; + var ref = numbersbBox[numbersAlign]; + if(numbersAlign === 'center') ref = (numbersbBox.left + numbersbBox.right) / 2; + var translateX = numbersX - scaleRatio * ref; + return strTranslate(translateX, translateY) + ' scale(' + scaleRatio + ')'; }); } diff --git a/test/image/baselines/gl3d_indicator_scatter3d.png b/test/image/baselines/gl3d_indicator_scatter3d.png index 9738f8f4d46..e3e385434d1 100644 Binary files a/test/image/baselines/gl3d_indicator_scatter3d.png and b/test/image/baselines/gl3d_indicator_scatter3d.png differ diff --git a/test/image/baselines/indicator_bignumber.png b/test/image/baselines/indicator_bignumber.png index 5fa8ed5f271..5db81a4edc1 100644 Binary files a/test/image/baselines/indicator_bignumber.png and b/test/image/baselines/indicator_bignumber.png differ diff --git a/test/image/baselines/indicator_bullet.png b/test/image/baselines/indicator_bullet.png index ad4847f8a2f..f6f1502d95d 100644 Binary files a/test/image/baselines/indicator_bullet.png and b/test/image/baselines/indicator_bullet.png differ diff --git a/test/image/baselines/indicator_datacard.png b/test/image/baselines/indicator_datacard.png index 27378fcf290..fad33d083f8 100644 Binary files a/test/image/baselines/indicator_datacard.png and b/test/image/baselines/indicator_datacard.png differ diff --git a/test/image/baselines/indicator_datacard2.png b/test/image/baselines/indicator_datacard2.png index 22648e8179b..17c11f6e7a1 100644 Binary files a/test/image/baselines/indicator_datacard2.png and b/test/image/baselines/indicator_datacard2.png differ diff --git a/test/image/baselines/indicator_datacard3.png b/test/image/baselines/indicator_datacard3.png new file mode 100644 index 00000000000..fb959b51183 Binary files /dev/null and b/test/image/baselines/indicator_datacard3.png differ diff --git a/test/image/baselines/indicator_gauge.png b/test/image/baselines/indicator_gauge.png index 6f588335d61..8ea364ab649 100644 Binary files a/test/image/baselines/indicator_gauge.png and b/test/image/baselines/indicator_gauge.png differ diff --git a/test/image/baselines/indicator_grid_template.png b/test/image/baselines/indicator_grid_template.png index 1ff32b2836b..166e6ec025b 100644 Binary files a/test/image/baselines/indicator_grid_template.png and b/test/image/baselines/indicator_grid_template.png differ diff --git a/test/image/baselines/indicator_scatter.png b/test/image/baselines/indicator_scatter.png index 29bb15d6292..1d031bb97d8 100644 Binary files a/test/image/baselines/indicator_scatter.png and b/test/image/baselines/indicator_scatter.png differ diff --git a/test/image/mocks/indicator_datacard.json b/test/image/mocks/indicator_datacard.json index f2a34579967..de57046ebb6 100644 --- a/test/image/mocks/indicator_datacard.json +++ b/test/image/mocks/indicator_datacard.json @@ -4,7 +4,6 @@ "type": "indicator", "mode": "number+delta", "number": { - "align": "middle", "suffix": " km/h" }, "value": 43, @@ -17,7 +16,6 @@ "type": "indicator", "mode": "number+delta", "number": { - "align": "middle", "suffix": " km/h" }, "value": 43, @@ -30,7 +28,6 @@ "type": "indicator", "mode": "number+delta", "number": { - "align": "middle", "suffix": " km/h" }, "value": 43, @@ -43,7 +40,6 @@ "type": "indicator", "mode": "number+delta", "number": { - "align": "middle", "suffix": " km/h" }, "value": 43, diff --git a/test/image/mocks/indicator_datacard3.json b/test/image/mocks/indicator_datacard3.json new file mode 100644 index 00000000000..9d004eaf471 --- /dev/null +++ b/test/image/mocks/indicator_datacard3.json @@ -0,0 +1,140 @@ +{ + "data": [{ + "type": "indicator", + "domain": {"row": 0, "column": 0}, + "delta": {"position": "left"}, + "align": "left" + }, { + "type": "indicator", + "domain": {"x": [0.05, 0.5], "y": [0.15, 0.35]}, + "domain": {"row": 0, "column": 1}, + "delta": {"position": "top"}, + "align": "left" + }, { + "type": "indicator", + "domain": {"row": 0, "column": 2}, + "delta": {"position": "right"}, + "align": "left" + }, { + "type": "indicator", + "domain": {"row": 0, "column": 3}, + "delta": {"position": "bottom"}, + "align": "left" + }, + + { + "type": "indicator", + "domain": {"row": 1, "column": 0}, + "delta": {"position": "left"}, + "align": "center" + }, { + "type": "indicator", + "domain": {"x": [0.05, 0.5], "y": [0.15, 0.35]}, + "domain": {"row": 1, "column": 1}, + "delta": {"position": "top"}, + "align": "center" + }, { + "type": "indicator", + "domain": {"row": 1, "column": 2}, + "delta": {"position": "right"}, + "align": "center" + }, { + "type": "indicator", + "domain": {"row": 1, "column": 3}, + "delta": {"position": "bottom"}, + "align": "center" + }, + + { + "type": "indicator", + "domain": {"row": 2, "column": 0}, + "delta": {"position": "left"}, + "align": "right" + }, { + "type": "indicator", + "domain": {"x": [0.05, 0.5], "y": [0.15, 0.35]}, + "domain": {"row": 2, "column": 1}, + "delta": {"position": "top"}, + "align": "right" + }, { + "type": "indicator", + "domain": {"row": 2, "column": 2}, + "delta": {"position": "right"}, + "align": "right" + }, { + "type": "indicator", + "domain": {"row": 2, "column": 3}, + "delta": {"position": "bottom"}, + "align": "right" + } +], + "layout": { + "width": 1000, + "height": 200, + "margin": {"t": 0, "b": 0, "l": 0, "r": 0}, + "grid": {"rows": 3, "columns": 4, "pattern": "independent", "xgap": 0, "ygap": 0}, + "shapes": [{ + "type": "line", + "x0": 0, + "x1": 1, + "y0": 0.33, + "y1": 0.33, + "line": { + "color": "rgba(0, 0, 0, 0.35)", + "width": 1 + } + }, + { + "type": "line", + "x0": 0, + "x1": 1, + "y0": 0.66, + "y1": 0.66, + "line": { + "color": "rgba(0, 0, 0, 0.35)", + "width": 1 + } + }, + { + "type": "line", + "x0": 0.25, + "x1": 0.25, + "y0": 0, + "y1": 1, + "line": { + "color": "rgba(0, 0, 0, 0.35)", + "width": 1 + } + }, + { + "type": "line", + "x0": 0.5, + "x1": 0.5, + "y0": 0, + "y1": 1, + "line": { + "color": "rgba(0, 0, 0, 0.35)", + "width": 1 + } + },{ + "type": "line", + "x0": 0.75, + "x1": 0.75, + "y0": 0, + "y1": 1, + "line": { + "color": "rgba(0, 0, 0, 0.35)", + "width": 1 + } + }], + "template": { + "data": { + "indicator": [{ + "mode": "number+delta", + "delta": {"reference": 60}, + "value": 120 + }] + } + } + } +} diff --git a/test/jasmine/tests/indicator_test.js b/test/jasmine/tests/indicator_test.js index 9b298ea32bf..fc971dba985 100644 --- a/test/jasmine/tests/indicator_test.js +++ b/test/jasmine/tests/indicator_test.js @@ -143,7 +143,7 @@ describe('Indicator plot', function() { describe('numbers', function() { function checkNumbersScale(value, msg) { - var numbers = d3.selectAll('text.numbers'); + var numbers = d3.selectAll('g.numbers'); expect(numbers.length).toBe(1); var transform = numbers.attr('transform'); @@ -220,39 +220,11 @@ describe('Indicator plot', function() { .then(done); }); }); - - it('always positions tspans in the right order', function(done) { - Plotly.newPlot(gd, [{ - type: 'indicator', - value: 10 - }]) - .then(function() { - customAssertions.assertMultiNodeOrder(['tspan.number']); - return Plotly.restyle(gd, 'mode', 'delta'); - }) - .then(function() { - customAssertions.assertMultiNodeOrder(['tspan.delta']); - return Plotly.restyle(gd, 'mode', 'number+delta'); - }) - .then(function() { - customAssertions.assertMultiNodeOrder(['tspan.number', 'tspan.delta']); - return Plotly.restyle(gd, 'delta.position', 'left'); - }) - .then(function() { - customAssertions.assertMultiNodeOrder(['tspan.delta', 'tspan.number']); - return Plotly.restyle(gd, 'mode', 'gauge'); - }) - .then(function() { - customAssertions.assertMultiNodeOrder([]); - }) - .catch(failTest) - .then(done); - }); }); describe('number', function() { function assertContent(txt) { - var sel = d3.selectAll('tspan.number'); + var sel = d3.selectAll('text.number'); expect(sel.length).toBe(1); expect(sel.text()).toBe(txt); } @@ -308,7 +280,7 @@ describe('Indicator plot', function() { describe('delta', function() { function assertContent(txt) { - var sel = d3.selectAll('tspan.delta'); + var sel = d3.selectAll('text.delta'); expect(sel.length).toBe(1); expect(sel.text()).toBe(txt); } @@ -402,7 +374,7 @@ describe('Indicator plot', function() { var t = d3.selectAll('text.title').node(); var titleBBox = t.getBoundingClientRect(); - var numbers = d3.selectAll('text.numbers').node(); + var numbers = d3.selectAll('g.numbers').node(); var numbersBBox = numbers.getBoundingClientRect(); expect(titleBBox.bottom).toBeCloseTo(numbersBBox.top - cn.titlePadding, 0); @@ -465,7 +437,7 @@ describe('Indicator plot', function() { } function assert(flags) { // flags is an array denoting whether the figure [hasNumber, hasDelta, hasAngular, hasBullet] - var selector = ['tspan.number', 'tspan.delta', 'g.angular', 'g.bullet']; + var selector = ['text.number', 'text.delta', 'g.angular', 'g.bullet']; [0, 1].forEach(function(i) { assertElementCnt(selector[i], flags[i]);}); [2, 3].forEach(function(i) { assertGauge(selector[i], flags[i]);}); diff --git a/test/jasmine/tests/snapshot_test.js b/test/jasmine/tests/snapshot_test.js index 6c4a4dc3fb0..62743768fca 100644 --- a/test/jasmine/tests/snapshot_test.js +++ b/test/jasmine/tests/snapshot_test.js @@ -255,35 +255,6 @@ describe('Plotly.Snapshot', function() { expect((actual || '').substr(0, 6)).toBe('url(\"#', msg); } - it('- tspans', function(done) { - var fontFamily = '"Times New Roman"'; - Plotly.newPlot(gd, [{ - type: 'indicator', - mode: 'delta+number', - value: 10 - }], {font: {family: fontFamily}}) - .then(function() { - d3.selectAll('tspan').each(function() { - expect(this.style.fontFamily).toEqual(fontFamily); - }); - - return Plotly.Snapshot.toSVG(gd); - }) - .then(function(svg) { - var svgDOM = parser.parseFromString(svg, 'image/svg+xml'); - var i; - - var tspanElements = svgDOM.getElementsByTagName('tspan'); - expect(tspanElements.length).toEqual(2); - - for(i = 0; i < tspanElements.length; i++) { - expect(tspanElements[i].style.fontFamily).toEqual(fontFamily); - } - }) - .catch(failTest) - .then(done); - }); - it('- marker-gradient case', function(done) { Plotly.plot(gd, [{ y: [1, 2, 1],