diff --git a/src/components/drawing/index.js b/src/components/drawing/index.js index c6213e9f219..b949d8d8393 100644 --- a/src/components/drawing/index.js +++ b/src/components/drawing/index.js @@ -443,12 +443,10 @@ drawing.tryColorscale = function(marker, prefix) { var TEXTOFFSETSIGN = {start: 1, end: -1, middle: 0, bottom: 1, top: -1}; drawing.textPointStyle = function(s, trace, gd) { s.each(function(d) { - var p = d3.select(this), - text = d.tx || trace.text; + var p = d3.select(this); + var text = Lib.extractOption(d, trace, 'tx', 'text'); - if(!text || Array.isArray(text)) { - // isArray test handles the case of (intentionally) missing - // or empty text within a text array + if(!text) { p.remove(); return; } @@ -889,6 +887,9 @@ drawing.setTextPointsScale = function(selection, xScale, yScale) { var transforms; var el = d3.select(this); var text = el.select('text'); + + if(!text.node()) return; + var x = parseFloat(text.attr('x') || 0); var y = parseFloat(text.attr('y') || 0); diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index 612ce0200c4..bc5866198bd 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -1028,24 +1028,22 @@ function alignHoverText(hoverLabels, rotateLabels) { } function cleanPoint(d, hovermode) { + var index = d.index; var trace = d.trace || {}; var cd0 = d.cd[0]; - var cd = d.cd[d.index] || {}; + var cd = d.cd[index] || {}; + + var getVal = Array.isArray(index) ? + function(calcKey, traceKey) { + return Lib.castOption(cd0, index, calcKey) || + Lib.extractOption({}, trace, '', traceKey); + } : + function(calcKey, traceKey) { + return Lib.extractOption(cd, trace, calcKey, traceKey); + }; function fill(key, calcKey, traceKey) { - var val; - - if(cd[calcKey]) { - val = cd[calcKey]; - } else if(cd0[calcKey]) { - var arr = cd0[calcKey]; - if(Array.isArray(arr) && Array.isArray(arr[d.index[0]])) { - val = arr[d.index[0]][d.index[1]]; - } - } else { - val = Lib.nestedProperty(trace, traceKey).get(); - } - + var val = getVal(calcKey, traceKey); if(val) d[key] = val; } diff --git a/src/lib/index.js b/src/lib/index.js index 8e0e2abe17f..cbfc190adb3 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -437,6 +437,26 @@ lib.castOption = function(trace, ptNumber, astr, fn) { } }; +/** Extract option from calcdata item, correctly falling back to + * trace value if not found. + * + * @param {object} calcPt : calcdata[i][j] item + * @param {object} trace : (full) trace object + * @param {string} calcKey : calcdata key + * @param {string} traceKey : aka trace attribute string + * @return {any} + */ +lib.extractOption = function(calcPt, trace, calcKey, traceKey) { + if(calcKey in calcPt) return calcPt[calcKey]; + + // fallback to trace value, + // must check if value isn't itself an array + // which means the trace attribute has a corresponding + // calcdata key, but its value is falsy + var traceVal = lib.nestedProperty(trace, traceKey).get(); + if(!Array.isArray(traceVal)) return traceVal; +}; + /** Returns target as set by 'target' transform attribute * * @param {object} trace : full trace object diff --git a/src/traces/bar/hover.js b/src/traces/bar/hover.js index b718dc4b139..971798d6334 100644 --- a/src/traces/bar/hover.js +++ b/src/traces/bar/hover.js @@ -12,7 +12,7 @@ var Fx = require('../../components/fx'); var ErrorBars = require('../../components/errorbars'); var Color = require('../../components/color'); - +var fillHoverText = require('../scatter/fill_hover_text'); module.exports = function hoverPoints(pointData, xval, yval, hovermode) { var cd = pointData.cd; @@ -99,11 +99,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { pointData.xLabelVal = di.p; } - if(di.htx) pointData.text = di.htx; - else if(trace.hovertext) pointData.text = trace.hovertext; - else if(di.tx) pointData.text = di.tx; - else if(trace.text) pointData.text = trace.text; - + fillHoverText(di, trace, pointData); ErrorBars.hoverInfo(di, trace, pointData); return [pointData]; diff --git a/src/traces/choropleth/attributes.js b/src/traces/choropleth/attributes.js index 8c29ff7c384..cab58297206 100644 --- a/src/traces/choropleth/attributes.js +++ b/src/traces/choropleth/attributes.js @@ -34,11 +34,9 @@ module.exports = extendFlat({ editType: 'calc', description: 'Sets the color values.' }, - text: { - valType: 'data_array', - editType: 'calc', + text: extendFlat({}, ScatterGeoAttrs.text, { description: 'Sets the text elements associated with each location.' - }, + }), marker: { line: { color: ScatterGeoMarkerLineAttrs.color, diff --git a/src/traces/choropleth/hover.js b/src/traces/choropleth/hover.js index 71799f22385..deda1b54b7e 100644 --- a/src/traces/choropleth/hover.js +++ b/src/traces/choropleth/hover.js @@ -11,6 +11,7 @@ var Axes = require('../../plots/cartesian/axes'); var attributes = require('./attributes'); +var fillHoverText = require('../scatter/fill_hover_text'); module.exports = function hoverPoints(pointData, xval, yval) { var cd = pointData.cd; @@ -53,17 +54,17 @@ module.exports = function hoverPoints(pointData, xval, yval) { }; function makeHoverInfo(pointData, trace, pt, axis) { - var hoverinfo = trace.hoverinfo; + var hoverinfo = pt.hi || trace.hoverinfo; var parts = (hoverinfo === 'all') ? attributes.hoverinfo.flags : hoverinfo.split('+'); - var hasName = (parts.indexOf('name') !== -1), - hasLocation = (parts.indexOf('location') !== -1), - hasZ = (parts.indexOf('z') !== -1), - hasText = (parts.indexOf('text') !== -1), - hasIdAsNameLabel = !hasName && hasLocation; + var hasName = (parts.indexOf('name') !== -1); + var hasLocation = (parts.indexOf('location') !== -1); + var hasZ = (parts.indexOf('z') !== -1); + var hasText = (parts.indexOf('text') !== -1); + var hasIdAsNameLabel = !hasName && hasLocation; var text = []; @@ -79,7 +80,9 @@ function makeHoverInfo(pointData, trace, pt, axis) { } if(hasZ) text.push(formatter(pt.z)); - if(hasText) text.push(pt.tx); + if(hasText) { + fillHoverText(pt, trace, text); + } pointData.extraText = text.join('
'); } diff --git a/src/traces/scatter/fill_hover_text.js b/src/traces/scatter/fill_hover_text.js new file mode 100644 index 00000000000..945f8e8960e --- /dev/null +++ b/src/traces/scatter/fill_hover_text.js @@ -0,0 +1,41 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Lib = require('../../lib'); + +/** Fill hover 'pointData' container with 'correct' hover text value + * + * - If trace hoverinfo contains a 'text' flag and hovertext is not set, + * the text elements will be seen in the hover labels. + * + * - If trace hoverinfo contains a 'text' flag and hovertext is set, + * hovertext takes precedence over text + * i.e. the hoverinfo elements will be seen in the hover labels + * + * @param {object} calcPt + * @param {object} trace + * @param {object || array} contOut (mutated here) + */ +module.exports = function fillHoverText(calcPt, trace, contOut) { + var fill = Array.isArray(contOut) ? + function(v) { contOut.push(v); } : + function(v) { contOut.text = v; }; + + var htx = Lib.extractOption(calcPt, trace, 'htx', 'hovertext'); + if(isValid(htx)) return fill(htx); + + var tx = Lib.extractOption(calcPt, trace, 'tx', 'text'); + if(isValid(tx)) return fill(tx); +}; + +// accept all truthy values and 0 (which gets cast to '0' in the hover labels) +function isValid(v) { + return v || v === 0; +} diff --git a/src/traces/scatter/hover.js b/src/traces/scatter/hover.js index ee968926c64..601a30c2e47 100644 --- a/src/traces/scatter/hover.js +++ b/src/traces/scatter/hover.js @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; var Lib = require('../../lib'); @@ -14,6 +13,7 @@ var Fx = require('../../components/fx'); var ErrorBars = require('../../components/errorbars'); var getTraceColor = require('./get_trace_color'); var Color = require('../../components/color'); +var fillHoverText = require('./fill_hover_text'); var MAXDIST = Fx.constants.MAXDIST; @@ -73,11 +73,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { yLabelVal: di.y }); - if(di.htx) pointData.text = di.htx; - else if(trace.hovertext) pointData.text = trace.hovertext; - else if(di.tx) pointData.text = di.tx; - else if(trace.text) pointData.text = trace.text; - + fillHoverText(di, trace, pointData); ErrorBars.hoverInfo(di, trace, pointData); return [pointData]; diff --git a/src/traces/scattercarpet/hover.js b/src/traces/scattercarpet/hover.js index 980072cb12e..5e0bf79d29f 100644 --- a/src/traces/scattercarpet/hover.js +++ b/src/traces/scattercarpet/hover.js @@ -46,18 +46,27 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { newPointData.yLabelVal = undefined; // TODO: nice formatting, and label by axis title, for a, b, and c? - var trace = newPointData.trace, - carpet = trace._carpet, - hoverinfo = trace.hoverinfo.split('+'), - text = []; + var trace = newPointData.trace; + var carpet = trace._carpet; + var hoverinfo = cdi.hi || trace.hoverinfo; + var parts = hoverinfo.split('+'); + var text = []; function textPart(ax, val) { - text.push(((ax.labelprefix && ax.labelprefix.length > 0) ? ax.labelprefix : (ax._hovertitle + ': ')) + val.toFixed(3) + ax.labelsuffix); + var prefix; + + if(ax.labelprefix && ax.labelprefix.length > 0) { + prefix = ax.labelprefix.replace(/ = $/, ''); + } else { + prefix = ax._hovertitle; + } + + text.push(prefix + ': ' + val.toFixed(3) + ax.labelsuffix); } - if(hoverinfo.indexOf('all') !== -1) hoverinfo = ['a', 'b']; - if(hoverinfo.indexOf('a') !== -1) textPart(carpet.aaxis, cdi.a); - if(hoverinfo.indexOf('b') !== -1) textPart(carpet.baxis, cdi.b); + if(parts.indexOf('all') !== -1) parts = ['a', 'b']; + if(parts.indexOf('a') !== -1) textPart(carpet.aaxis, cdi.a); + if(parts.indexOf('b') !== -1) textPart(carpet.baxis, cdi.b); var ij = carpet.ab2ij([cdi.a, cdi.b]); var i0 = Math.floor(ij[0]); diff --git a/src/traces/scattergeo/hover.js b/src/traces/scattergeo/hover.js index 4d3135d5d2a..56aa35c359d 100644 --- a/src/traces/scattergeo/hover.js +++ b/src/traces/scattergeo/hover.js @@ -14,9 +14,9 @@ var Axes = require('../../plots/cartesian/axes'); var BADNUM = require('../../constants/numerical').BADNUM; var getTraceColor = require('../scatter/get_trace_color'); +var fillHoverText = require('../scatter/fill_hover_text'); var attributes = require('./attributes'); - module.exports = function hoverPoints(pointData, xval, yval) { var cd = pointData.cd; var trace = cd[0].trace; @@ -71,39 +71,34 @@ module.exports = function hoverPoints(pointData, xval, yval) { }; function getExtraText(trace, pt, axis) { - var hoverinfo = trace.hoverinfo; + var hoverinfo = pt.hi || trace.hoverinfo; - var parts = (hoverinfo === 'all') ? + var parts = hoverinfo === 'all' ? attributes.hoverinfo.flags : hoverinfo.split('+'); - var hasLocation = parts.indexOf('location') !== -1 && Array.isArray(trace.locations), - hasLon = (parts.indexOf('lon') !== -1), - hasLat = (parts.indexOf('lat') !== -1), - hasText = (parts.indexOf('text') !== -1); - + var hasLocation = parts.indexOf('location') !== -1 && Array.isArray(trace.locations); + var hasLon = (parts.indexOf('lon') !== -1); + var hasLat = (parts.indexOf('lat') !== -1); + var hasText = (parts.indexOf('text') !== -1); var text = []; function format(val) { return Axes.tickText(axis, axis.c2l(val), 'hover').text + '\u00B0'; } - if(hasLocation) text.push(pt.loc); - else if(hasLon && hasLat) { + if(hasLocation) { + text.push(pt.loc); + } else if(hasLon && hasLat) { text.push('(' + format(pt.lonlat[0]) + ', ' + format(pt.lonlat[1]) + ')'); + } else if(hasLon) { + text.push('lon: ' + format(pt.lonlat[0])); + } else if(hasLat) { + text.push('lat: ' + format(pt.lonlat[1])); } - else if(hasLon) text.push('lon: ' + format(pt.lonlat[0])); - else if(hasLat) text.push('lat: ' + format(pt.lonlat[1])); if(hasText) { - var tx; - - if(pt.htx) tx = pt.htx; - else if(trace.hovertext) tx = trace.hovertext; - else if(pt.tx) tx = pt.tx; - else if(trace.text) tx = trace.text; - - if(!Array.isArray(tx)) text.push(tx); + fillHoverText(pt, trace, text); } return text.join('
'); diff --git a/src/traces/scattermapbox/hover.js b/src/traces/scattermapbox/hover.js index 3b3f5434634..d9805ed152a 100644 --- a/src/traces/scattermapbox/hover.js +++ b/src/traces/scattermapbox/hover.js @@ -11,6 +11,7 @@ var Fx = require('../../components/fx'); var getTraceColor = require('../scatter/get_trace_color'); +var fillHoverText = require('../scatter/fill_hover_text'); var BADNUM = require('../../constants/numerical').BADNUM; module.exports = function hoverPoints(pointData, xval, yval) { @@ -66,13 +67,14 @@ module.exports = function hoverPoints(pointData, xval, yval) { }; function getExtraText(trace, di) { - var hoverinfo = trace.hoverinfo.split('+'), - isAll = (hoverinfo.indexOf('all') !== -1), - hasLon = (hoverinfo.indexOf('lon') !== -1), - hasLat = (hoverinfo.indexOf('lat') !== -1); + var hoverinfo = di.hi || trace.hoverinfo; + var parts = hoverinfo.split('+'); + var isAll = parts.indexOf('all') !== -1; + var hasLon = parts.indexOf('lon') !== -1; + var hasLat = parts.indexOf('lat') !== -1; - var lonlat = di.lonlat, - text = []; + var lonlat = di.lonlat; + var text = []; // TODO should we use a mock axis to format hover? // If so, we'll need to make precision be zoom-level dependent @@ -82,19 +84,14 @@ function getExtraText(trace, di) { if(isAll || (hasLon && hasLat)) { text.push('(' + format(lonlat[0]) + ', ' + format(lonlat[1]) + ')'); + } else if(hasLon) { + text.push('lon: ' + format(lonlat[0])); + } else if(hasLat) { + text.push('lat: ' + format(lonlat[1])); } - else if(hasLon) text.push('lon: ' + format(lonlat[0])); - else if(hasLat) text.push('lat: ' + format(lonlat[1])); - if(isAll || hoverinfo.indexOf('text') !== -1) { - var tx; - - if(di.htx) tx = di.htx; - else if(trace.hovertext) tx = trace.hovertext; - else if(di.tx) tx = di.tx; - else if(trace.text) tx = trace.text; - - if(!Array.isArray(tx)) text.push(tx); + if(isAll || parts.indexOf('text') !== -1) { + fillHoverText(di, trace, text); } return text.join('
'); diff --git a/src/traces/scatterternary/hover.js b/src/traces/scatterternary/hover.js index cdf459d2609..eed099c832d 100644 --- a/src/traces/scatterternary/hover.js +++ b/src/traces/scatterternary/hover.js @@ -49,19 +49,20 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { newPointData.yLabelVal = undefined; // TODO: nice formatting, and label by axis title, for a, b, and c? - var trace = newPointData.trace, - ternary = trace._ternary, - hoverinfo = trace.hoverinfo.split('+'), - text = []; + var trace = newPointData.trace; + var ternary = trace._ternary; + var hoverinfo = cdi.hi || trace.hoverinfo; + var parts = hoverinfo.split('+'); + var text = []; function textPart(ax, val) { text.push(ax._hovertitle + ': ' + Axes.tickText(ax, val, 'hover').text); } - if(hoverinfo.indexOf('all') !== -1) hoverinfo = ['a', 'b', 'c']; - if(hoverinfo.indexOf('a') !== -1) textPart(ternary.aaxis, cdi.a); - if(hoverinfo.indexOf('b') !== -1) textPart(ternary.baxis, cdi.b); - if(hoverinfo.indexOf('c') !== -1) textPart(ternary.caxis, cdi.c); + if(parts.indexOf('all') !== -1) parts = ['a', 'b', 'c']; + if(parts.indexOf('a') !== -1) textPart(ternary.aaxis, cdi.a); + if(parts.indexOf('b') !== -1) textPart(ternary.baxis, cdi.b); + if(parts.indexOf('c') !== -1) textPart(ternary.caxis, cdi.c); newPointData.extraText = text.join('
'); diff --git a/test/jasmine/assets/custom_assertions.js b/test/jasmine/assets/custom_assertions.js index 138020ac3a8..aa594999e82 100644 --- a/test/jasmine/assets/custom_assertions.js +++ b/test/jasmine/assets/custom_assertions.js @@ -59,6 +59,103 @@ exports.assertHoverLabelStyle = function(g, expectation, msg, textSelector) { expect(textStyle.fill).toBe(expectation.fontColor, msg + ': font.color'); }; +function assertLabelContent(label, expectation, msg) { + if(!expectation) expectation = ''; + + var lines = label.selectAll('tspan.line'); + var content = []; + + function fill(sel) { + if(sel.node()) { + var html = sel.html(); + if(html) content.push(html); + } + } + + if(lines.size()) { + lines.each(function() { fill(d3.select(this)); }); + } else { + fill(label); + } + + expect(content.join('\n')).toBe(expectation, msg + ': text content'); +} + +function count(selector) { + return d3.selectAll(selector).size(); +} + +/** + * @param {object} expectation + * - nums {string || array of strings} + * - name {string || array of strings} + * - axis {string} + * @param {string} msg + */ +exports.assertHoverLabelContent = function(expectation, msg) { + if(!msg) msg = ''; + + var ptSelector = 'g.hovertext'; + var ptMsg = msg + ' point hover label'; + var ptCnt = count(ptSelector); + + var axSelector = 'g.axistext'; + var axMsg = 'common axis hover label'; + var axCnt = count(axSelector); + + if(ptCnt === 1) { + assertLabelContent( + d3.select(ptSelector + '> text.nums'), + expectation.nums, + ptMsg + ' (nums)' + ); + assertLabelContent( + d3.select(ptSelector + '> text.name'), + expectation.name, + ptMsg + ' (name)' + ); + } else if(ptCnt > 1) { + if(!Array.isArray(expectation.nums) || !Array.isArray(expectation.name)) { + fail(ptMsg + ': expecting more than 1 labels.'); + } + + expect(ptCnt) + .toBe(expectation.name.length, ptMsg + ' # of visible labels'); + + d3.selectAll(ptSelector).each(function(_, i) { + assertLabelContent( + d3.select(this).select('text.nums'), + expectation.nums[i], + ptMsg + ' (nums ' + i + ')' + ); + assertLabelContent( + d3.select(this).select('text.name'), + expectation.name[i], + ptMsg + ' (name ' + i + ')' + ); + }); + } else { + if(expectation.nums) { + fail(ptMsg + ': expecting *nums* labels, did not find any.'); + } + if(expectation.name) { + fail(ptMsg + ': expecting *nums* labels, did not find any.'); + } + } + + if(axCnt) { + assertLabelContent( + d3.select(axSelector + '> text'), + expectation.axis, + axMsg + ); + } else { + if(expectation.axis) { + fail(axMsg + ': expecting label, did not find any.'); + } + } +}; + exports.assertClip = function(sel, isClipped, size, msg) { expect(sel.size()).toBe(size, msg + ' clip path (selection size)'); diff --git a/test/jasmine/tests/carpet_test.js b/test/jasmine/tests/carpet_test.js index a302de085e5..4e2164188da 100644 --- a/test/jasmine/tests/carpet_test.js +++ b/test/jasmine/tests/carpet_test.js @@ -11,6 +11,9 @@ var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); var fail = require('../assets/fail_test'); +var mouseEvent = require('../assets/mouse_event'); +var assertHoverLabelContent = require('../assets/custom_assertions').assertHoverLabelContent; + describe('carpet supplyDefaults', function() { 'use strict'; @@ -565,3 +568,53 @@ describe('scattercarpet array attributes', function() { .then(done); }); }); + +describe('scattercarpet hover labels', function() { + var gd; + + afterEach(destroyGraphDiv); + + function run(pos, fig, content) { + gd = createGraphDiv(); + + return Plotly.plot(gd, fig).then(function() { + mouseEvent('mousemove', pos[0], pos[1]); + assertHoverLabelContent({ + nums: content[0].join('\n'), + name: content[1] + }); + }); + } + + it('should generate hover label (base)', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/scattercarpet.json')); + + run( + [200, 200], fig, + [['a: 0.200', 'b: 3.500', 'y: 2.900'], 'a = 0.2'] + ) + .then(done); + }); + + it('should generate hover label with \'hoverinfo\' set', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/scattercarpet.json')); + fig.data[5].hoverinfo = 'a+y'; + + run( + [200, 200], fig, + [['a: 0.200', 'y: 2.900'], null] + ) + .then(done); + }); + + it('should generate hover label with arrayOk \'hoverinfo\' settings', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/scattercarpet.json')); + fig.data[5].hoverinfo = ['a+b', 'a+b', 'a+b', 'b+y']; + + run( + [200, 200], fig, + [['b: 3.500', 'y: 2.900'], null] + ) + .then(done); + }); +}); diff --git a/test/jasmine/tests/choropleth_test.js b/test/jasmine/tests/choropleth_test.js index 1cdcdcd28aa..1612bde7ae2 100644 --- a/test/jasmine/tests/choropleth_test.js +++ b/test/jasmine/tests/choropleth_test.js @@ -1,6 +1,17 @@ var Choropleth = require('@src/traces/choropleth'); + +var Plotly = require('@lib'); var Plots = require('@src/plots/plots'); +var Lib = require('@src/lib'); + +var d3 = require('d3'); +var createGraphDiv = require('../assets/create_graph_div'); +var destroyGraphDiv = require('../assets/destroy_graph_div'); +var mouseEvent = require('../assets/mouse_event'); +var customAssertions = require('../assets/custom_assertions'); +var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle; +var assertHoverLabelContent = customAssertions.assertHoverLabelContent; describe('Test choropleth', function() { 'use strict'; @@ -45,7 +56,107 @@ describe('Test choropleth', function() { Choropleth.supplyDefaults(traceIn, traceOut, defaultColor, layout); expect(traceOut.visible).toBe(false); }); + }); +}); + +describe('Test choropleth hover:', function() { + var gd; + + afterEach(destroyGraphDiv); + + function run(pos, fig, content, style) { + gd = createGraphDiv(); + + style = style || { + bgcolor: 'rgb(68, 68, 68)', + bordercolor: 'rgb(255, 255, 255)', + fontColor: 'rgb(255, 255, 255)', + fontSize: 13, + fontFamily: 'Arial' + }; + + return Plotly.plot(gd, fig).then(function() { + mouseEvent('mousemove', pos[0], pos[1]); + assertHoverLabelContent({ + nums: content[0], + name: content[1] + }); + assertHoverLabelStyle( + d3.select('g.hovertext'), + style + ); + }); + } + + it('should generate hover label info (base)', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/geo_first.json')); + run( + [400, 160], + fig, + ['RUS\n10', 'trace 1'] + ) + .then(done); }); + it('should generate hover label info (\'text\' single value case)', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/geo_first.json')); + fig.data[1].text = 'tExT'; + fig.data[1].hoverinfo = 'text'; + + run( + [400, 160], + fig, + ['tExT', null] + ) + .then(done); + }); + + it('should generate hover label info (\'text\' array case)', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/geo_first.json')); + fig.data[1].text = ['tExT', 'TeXt', '-text-']; + fig.data[1].hoverinfo = 'text'; + + run( + [400, 160], + fig, + ['-text-', null] + ) + .then(done); + }); + + it('should generate hover label with custom styling', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/geo_first.json')); + fig.data[1].hoverlabel = { + bgcolor: 'red', + bordercolor: ['blue', 'black', 'green'], + font: {family: 'Roboto'} + }; + + run( + [400, 160], + fig, + ['RUS\n10', 'trace 1'], + { + bgcolor: 'rgb(255, 0, 0)', + bordercolor: 'rgb(0, 128, 0)', + fontColor: 'rgb(0, 128, 0)', + fontSize: 13, + fontFamily: 'Roboto' + } + ) + .then(done); + }); + + it('should generate hover label with arrayOk \'hoverinfo\' settings', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/geo_first.json')); + fig.data[1].hoverinfo = ['location', 'z', 'location+name']; + + run( + [400, 160], + fig, + ['RUS', 'trace 1'] + ) + .then(done); + }); }); diff --git a/test/jasmine/tests/drawing_test.js b/test/jasmine/tests/drawing_test.js index 899ecef6527..5b5049857f5 100644 --- a/test/jasmine/tests/drawing_test.js +++ b/test/jasmine/tests/drawing_test.js @@ -345,6 +345,11 @@ describe('Drawing', function() { Drawing.setTextPointsScale(g, 4, 5); expect(g.attr('transform')).toEqual('translate(8,9) scale(4,5) translate(-8,-9) translate(1, 2)'); }); + + it('should not break when is not present', function() { + text.remove(); + expect(function() { Drawing.setTextPointsScale(g, 4, 5); }).not.toThrow(); + }); }); describe('bBox', function() { diff --git a/test/jasmine/tests/geo_test.js b/test/jasmine/tests/geo_test.js index 12d39069e89..bca4661146e 100644 --- a/test/jasmine/tests/geo_test.js +++ b/test/jasmine/tests/geo_test.js @@ -553,72 +553,6 @@ describe('Test geo interactions', function() { Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); }); - describe('scattergeo hover labels', function() { - it('should show one hover text group', function() { - mouseEventScatterGeo('mousemove'); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - }); - - it('should show longitude and latitude values', function() { - mouseEventScatterGeo('mousemove'); - - var node = d3.selectAll('g.hovertext').selectAll('tspan')[0][0]; - expect(node.innerHTML).toEqual('(0°, 0°)'); - }); - - it('should show the trace name', function() { - mouseEventScatterGeo('mousemove'); - - var node = d3.selectAll('g.hovertext').selectAll('text')[0][0]; - expect(node.innerHTML).toEqual('trace 0'); - }); - - it('should show *text* (case 1)', function(done) { - Plotly.restyle(gd, 'text', [['A', 'B']]).then(function() { - mouseEventScatterGeo('mousemove'); - - var node = d3.selectAll('g.hovertext').selectAll('tspan')[0][1]; - expect(node.innerHTML).toEqual('A'); - }) - .then(done); - }); - - it('should show *text* (case 2)', function(done) { - Plotly.restyle(gd, 'text', [[null, 'B']]).then(function() { - mouseEventScatterGeo('mousemove'); - - var node = d3.selectAll('g.hovertext').selectAll('tspan')[0][1]; - expect(node).toBeUndefined(); - }) - .then(done); - }); - - it('should show *text* (case 3)', function(done) { - Plotly.restyle(gd, 'text', [['', 'B']]).then(function() { - mouseEventScatterGeo('mousemove'); - - var node = d3.selectAll('g.hovertext').selectAll('tspan')[0][1]; - expect(node).toBeUndefined(); - }) - .then(done); - }); - - it('should show custom \`hoverlabel\' settings', function(done) { - Plotly.restyle(gd, { - 'hoverlabel.bgcolor': 'red', - 'hoverlabel.bordercolor': [['blue', 'black', 'green']] - }) - .then(function() { - mouseEventScatterGeo('mousemove'); - - var pathStyle = window.getComputedStyle(d3.select('g.hovertext path').node()); - expect(pathStyle.fill).toEqual('rgb(255, 0, 0)', 'bgcolor'); - expect(pathStyle.stroke).toEqual('rgb(0, 0, 255)', 'bordecolor[0]'); - }) - .then(done); - }); - }); - describe('scattergeo hover events', function() { var ptData, cnt; @@ -745,30 +679,6 @@ describe('Test geo interactions', function() { }); }); - describe('choropleth hover labels', function() { - beforeEach(function() { - mouseEventChoropleth('mouseover'); - mouseEventChoropleth('mousemove'); - }); - - it('should show one hover text group', function() { - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - }); - - it('should show location and z values', function() { - var node = d3.selectAll('g.hovertext').selectAll('tspan')[0]; - - expect(node[0].innerHTML).toEqual('RUS'); - expect(node[1].innerHTML).toEqual('10'); - }); - - it('should show the trace name', function() { - var node = d3.selectAll('g.hovertext').selectAll('text')[0][0]; - - expect(node.innerHTML).toEqual('trace 1'); - }); - }); - describe('choropleth hover events', function() { var ptData; diff --git a/test/jasmine/tests/gl2d_click_test.js b/test/jasmine/tests/gl2d_click_test.js index c966a3bc205..54dd23ee1aa 100644 --- a/test/jasmine/tests/gl2d_click_test.js +++ b/test/jasmine/tests/gl2d_click_test.js @@ -5,7 +5,10 @@ var d3 = require('d3'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); var fail = require('../assets/fail_test.js'); -var assertHoverLabelStyle = require('../assets/custom_assertions').assertHoverLabelStyle; + +var customAssertions = require('../assets/custom_assertions'); +var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle; +var assertHoverLabelContent = customAssertions.assertHoverLabelContent; // cartesian click events events use the hover data // from the mousemove events and then simulate @@ -131,25 +134,6 @@ describe('Test hover and click interactions', function() { expect(String(pt.pointNumber)).toBe(String(expected.pointNumber), msg + ' - point number'); } - function assertHoveLabelContent(expected) { - var label = expected.label; - - if(label === undefined) return; - - var g = d3.select('.hovertext'); - - if(label === null) { - expect(g.size()).toBe(0); - } else { - var lines = g.selectAll('text.nums'); - - expect(lines.size()).toBe(label.length); - lines.each(function(_, i) { - expect(d3.select(this).text()).toEqual(label[i]); - }); - } - } - // returns basic hover/click/unhover runner for one xy position function makeRunner(pos, expected, opts) { opts = opts || {}; @@ -166,12 +150,19 @@ describe('Test hover and click interactions', function() { .then(_hover) .then(function(eventData) { assertEventData(eventData, expected); + var g = d3.select('g.hovertext'); if(g.node() === null) { expect(expected.noHoverLabel).toBe(true); + } else { + assertHoverLabelStyle(g, expected, opts.msg); + } + if(expected.label) { + assertHoverLabelContent({ + nums: expected.label[0], + name: expected.label[1] + }); } - else assertHoverLabelStyle(g, expected, opts.msg); - assertHoveLabelContent(expected); }) .then(_click) .then(function(eventData) { @@ -211,7 +202,7 @@ describe('Test hover and click interactions', function() { var run = makeRunner([634, 321], { x: 15.772, y: 0.387, - label: ['0.387'], + label: ['0.387', null], curveNumber: 0, pointNumber: 33, bgcolor: 'rgb(0, 0, 255)', diff --git a/test/jasmine/tests/gl_plot_interact_test.js b/test/jasmine/tests/gl_plot_interact_test.js index c76bda805a5..8d2cf6a11c3 100644 --- a/test/jasmine/tests/gl_plot_interact_test.js +++ b/test/jasmine/tests/gl_plot_interact_test.js @@ -11,7 +11,10 @@ var fail = require('../assets/fail_test'); var mouseEvent = require('../assets/mouse_event'); var selectButton = require('../assets/modebar_button'); var delay = require('../assets/delay'); -var assertHoverLabelStyle = require('../assets/custom_assertions').assertHoverLabelStyle; + +var customAssertions = require('../assets/custom_assertions'); +var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle; +var assertHoverLabelContent = customAssertions.assertHoverLabelContent; function countCanvases() { return d3.selectAll('canvas').size(); @@ -33,17 +36,9 @@ describe('Test gl3d plots', function() { var mock3 = require('@mocks/gl3d_autocolorscale'); function assertHoverText(xLabel, yLabel, zLabel, textLabel) { - var node = d3.selectAll('g.hovertext'); - expect(node.size()).toEqual(1, 'hover text group'); - - var tspan = d3.selectAll('g.hovertext').selectAll('tspan')[0]; - expect(tspan[0].innerHTML).toEqual(xLabel, 'x val'); - expect(tspan[1].innerHTML).toEqual(yLabel, 'y val'); - expect(tspan[2].innerHTML).toEqual(zLabel, 'z val'); - - if(textLabel) { - expect(tspan[3].innerHTML).toEqual(textLabel, 'text label'); - } + var content = [xLabel, yLabel, zLabel]; + if(textLabel) content.push(textLabel); + assertHoverLabelContent({nums: content.join('\n')}); } function assertEventData(x, y, z, curveNumber, pointNumber, extra) { diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index df7bef8e771..b6be796bdbe 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -12,7 +12,10 @@ var click = require('../assets/click'); var delay = require('../assets/delay'); var doubleClick = require('../assets/double_click'); var fail = require('../assets/fail_test'); -var assertHoverLabelStyle = require('../assets/custom_assertions').assertHoverLabelStyle; + +var customAssertions = require('../assets/custom_assertions'); +var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle; +var assertHoverLabelContent = customAssertions.assertHoverLabelContent; describe('hover info', function() { 'use strict'; @@ -40,10 +43,10 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - expect(d3.selectAll('g.axistext').size()).toEqual(1); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388'); - expect(d3.selectAll('g.hovertext').select('text').html()).toEqual('1'); + assertHoverLabelContent({ + nums: '1', + axis: '0.388' + }); }); }); @@ -67,9 +70,7 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - expect(d3.selectAll('g.axistext').size()).toEqual(1); - expect(d3.selectAll('g.hovertext').size()).toEqual(0); - expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388'); + assertHoverLabelContent({axis: '0.388'}); }); }); @@ -93,9 +94,7 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - expect(d3.selectAll('g.axistext').size()).toEqual(0); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - expect(d3.selectAll('g.hovertext').select('text').html()).toEqual('1'); + assertHoverLabelContent({nums: '1'}); }); }); @@ -123,10 +122,9 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - expect(d3.selectAll('g.axistext').size()).toEqual(0); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - expect(d3.selectAll('g.hovertext').select('text').html()) - .toEqual('hover text with spaces not newlines'); + assertHoverLabelContent({ + nums: 'hover text with spaces not newlines' + }); }); }); @@ -152,12 +150,11 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - expect(d3.selectAll('g.axistext').size()).toEqual(1); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388'); - expect(d3.selectAll('g.hovertext').select('text.nums').selectAll('tspan').size()).toEqual(2); - expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][0].innerHTML).toEqual('1'); - expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][1].innerHTML).toEqual('hover text'); + assertHoverLabelContent({ + nums: '1\nhover text', + name: 'PV learning ...', + axis: '0.388' + }); }); }); @@ -190,13 +187,11 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - expect(d3.selectAll('g.axistext').size()).toEqual(1); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388'); - expect(d3.selectAll('g.hovertext').select('text.nums').selectAll('tspan').size()).toEqual(2); - expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][0].innerHTML).toEqual('1'); - expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][1].innerHTML).toEqual('hover text'); - expect(d3.selectAll('g.hovertext').selectAll('text.name').node().innerHTML).toEqual('<img src=x o...'); + assertHoverLabelContent({ + nums: '1\nhover text', + name: '<img src=x o...', + axis: '0.388' + }); }); }); @@ -213,10 +208,10 @@ describe('hover info', function() { Plotly.plot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); }); - it('responds to hover y+text', function() { + it('responds to hover y', function() { Fx.hover('graph', evt, 'xy'); - expect(d3.selectAll('g.hovertext').selectAll('text.nums').node().innerHTML).toEqual('1e+9'); + assertHoverLabelContent({nums: '1e+9'}); }); }); @@ -242,11 +237,9 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - expect(d3.selectAll('g.axistext').size()).toEqual(0); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - expect(d3.selectAll('g.hovertext').selectAll('tspan').size()).toEqual(2); - expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][0].innerHTML).toEqual('1'); - expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][1].innerHTML).toEqual('hover text'); + assertHoverLabelContent({ + nums: '1\nhover text' + }); }); }); @@ -272,10 +265,10 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - expect(d3.selectAll('g.axistext').size()).toEqual(1); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388'); - expect(d3.selectAll('g.hovertext').select('text').html()).toEqual('hover text'); + assertHoverLabelContent({ + nums: 'hover text', + axis: '0.388' + }); }); }); @@ -292,8 +285,10 @@ describe('hover info', function() { it('responds to hover x+text', function() { Fx.hover('graph', evt, 'xy'); - expect(d3.selectAll('g.axistext').size()).toEqual(1); - expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388 ± 1'); + assertHoverLabelContent({ + nums: '1', + axis: '0.388 ± 1' + }); }); }); @@ -310,8 +305,10 @@ describe('hover info', function() { it('responds to hover x+text', function() { Fx.hover('graph', evt, 'xy'); - expect(d3.selectAll('g.axistext').size()).toEqual(1); - expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388'); + assertHoverLabelContent({ + nums: '1', + axis: '0.388' + }); }); }); @@ -328,8 +325,10 @@ describe('hover info', function() { it('responds to hover x+text', function() { Fx.hover('graph', evt, 'xy'); - expect(d3.selectAll('g.axistext').size()).toEqual(1); - expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388'); + assertHoverLabelContent({ + nums: '1', + axis: '0.388' + }); }); }); @@ -355,11 +354,9 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - expect(d3.selectAll('g.axistext').size()).toEqual(0); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][0].innerHTML).toEqual('hover'); - expect(d3.selectAll('g.hovertext').selectAll('tspan')[0][1].innerHTML).toEqual('text'); - expect(d3.selectAll('g.hovertext').select('text').selectAll('tspan').size()).toEqual(2); + assertHoverLabelContent({ + nums: 'hover\ntext' + }); }); }); @@ -377,6 +374,7 @@ describe('hover info', function() { Fx.hover('graph', evt, 'xy'); expect(gd._hoverdata, undefined); + assertHoverLabelContent({}); }); }); @@ -400,8 +398,7 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - expect(d3.selectAll('g.axistext').size()).toEqual(0); - expect(d3.selectAll('g.hovertext').size()).toEqual(0); + assertHoverLabelContent({}); }); }); @@ -432,12 +429,9 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.33); expect(hoverTrace.y).toEqual(1.25); - expect(d3.selectAll('g.axistext').size()).toEqual(0); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - - var expectations = ['PV learning ...', '(0.33, 1.25)']; - d3.selectAll('g.hovertext').selectAll('text').each(function(_, i) { - expect(d3.select(this).html()).toEqual(expectations[i]); + assertHoverLabelContent({ + nums: '(0.33, 1.25)', + name: 'PV learning ...' }); }); @@ -456,15 +450,11 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.33); expect(hoverTrace.y).toEqual(1.25); - expect(d3.selectAll('g.axistext').size()).toEqual(0); - expect(d3.selectAll('g.hovertext').size()).toEqual(1); - - var text = d3.selectAll('g.hovertext').select('text'); - expect(text.size()).toEqual(1); - expect(text.html()).toEqual('PV learning ...'); - - done(); - }); + assertHoverLabelContent({ + nums: 'PV learning ...' + }); + }) + .then(done); }); }); @@ -474,16 +464,6 @@ describe('hover info', function() { Lib.clearThrottle(); } - function _assert(nameLabel, lines) { - expect(d3.select('g.axistext').size()).toEqual(0, 'no common label'); - - var sel = d3.select('g.hovertext'); - expect(sel.select('text.name').html()).toEqual(nameLabel, 'name label'); - sel.select('text.nums').selectAll('tspan').each(function(_, i) { - expect(d3.select(this).html()).toEqual(lines[i], 'lines ' + i); - }); - } - it('should display correct label content', function(done) { var gd = createGraphDiv(); @@ -504,11 +484,17 @@ describe('hover info', function() { }) .then(function() { _hover(gd, 250, 100); - _assert('two', ['x: 1', 'y: 3', 'z: 2']); + assertHoverLabelContent({ + nums: 'x: 1\ny: 3\nz: 2', + name: 'two' + }); }) .then(function() { _hover(gd, 250, 300); - _assert('one', ['x: 1', 'y: 1', 'z: 2']); + assertHoverLabelContent({ + nums: 'x: 1\ny: 1\nz: 2', + name: 'one' + }); }) .catch(fail) .then(done); @@ -516,7 +502,6 @@ describe('hover info', function() { }); describe('hoverformat', function() { - var data = [{ x: [1, 2, 3], y: [0.12345, 0.23456, 0.34567] @@ -535,10 +520,10 @@ describe('hover info', function() { Plotly.plot(this.gd, data, layout); mouseEvent('mousemove', 303, 213); - var hovers = d3.selectAll('g.hovertext'); - - expect(hovers.size()).toEqual(1); - expect(hovers.select('text')[0][0].textContent).toEqual('0.23'); + assertHoverLabelContent({ + nums: '0.23', + axis: '2' + }); }); it('should display the correct format when ticklabels false', function() { @@ -546,15 +531,14 @@ describe('hover info', function() { Plotly.plot(this.gd, data, layout); mouseEvent('mousemove', 303, 213); - var hovers = d3.selectAll('g.hovertext'); - - expect(hovers.size()).toEqual(1); - expect(hovers.select('text')[0][0].textContent).toEqual('0.23'); + assertHoverLabelContent({ + nums: '0.23', + axis: '2' + }); }); }); describe('textmode', function() { - var data = [{ x: [1, 2, 3, 4], y: [2, 3, 4, 5], @@ -573,28 +557,26 @@ describe('hover info', function() { it('should show text labels', function() { mouseEvent('mousemove', 108, 303); - var hovers = d3.selectAll('g.hovertext'); - expect(hovers.size()).toEqual(1); - expect(hovers.select('text')[0][0].textContent).toEqual('test'); + assertHoverLabelContent({ + nums: 'test' + }); }); it('should show number labels', function() { mouseEvent('mousemove', 363, 173); - var hovers = d3.selectAll('g.hovertext'); - expect(hovers.size()).toEqual(1); - expect(hovers.select('text')[0][0].textContent).toEqual('42'); + assertHoverLabelContent({ + nums: '42' + }); }); it('should not show null text labels', function() { mouseEvent('mousemove', 229, 239); - var hovers = d3.selectAll('g.hovertext'); - expect(hovers.size()).toEqual(0); + assertHoverLabelContent({}); }); it('should not show undefined text labels', function() { mouseEvent('mousemove', 493, 108); - var hovers = d3.selectAll('g.hovertext'); - expect(hovers.size()).toEqual(0); + assertHoverLabelContent({}); }); }); @@ -688,18 +670,14 @@ describe('hover info on stacked subplots', function() { y: 1000 })); - // There should be a single label on the x-axis with the shared x value, 3. - expect(d3.selectAll('g.axistext').size()).toEqual(1); - expect(d3.selectAll('g.axistext').select('text').html()).toEqual('3'); - - // There should be two points being hovered over, in two different traces, one in each plot. - expect(d3.selectAll('g.hovertext').size()).toEqual(2); - var textNodes = d3.selectAll('g.hovertext').selectAll('text'); - - expect(textNodes[0][0].innerHTML).toEqual('trace 1'); - expect(textNodes[0][1].innerHTML).toEqual('110'); - expect(textNodes[1][0].innerHTML).toEqual('trace 2'); - expect(textNodes[1][1].innerHTML).toEqual('1000'); + assertHoverLabelContent({ + // There should be 2 pts being hovered over, + // in two different traces, one in each plot. + nums: ['110', '1000'], + name: ['trace 1', 'trace 2'], + // There should be a single label on the x-axis with the shared x value, 3' + axis: '3' + }); }); }); @@ -746,20 +724,15 @@ describe('hover info on stacked subplots', function() { y: 0 })); - // There should be a single label on the y-axis with the shared y value, 0. - expect(d3.selectAll('g.axistext').size()).toEqual(1); - expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0'); - - // There should be three points being hovered over, in three different traces, one in each plot. - expect(d3.selectAll('g.hovertext').size()).toEqual(3); - var textNodes = d3.selectAll('g.hovertext').selectAll('text'); - expect(textNodes[0][0].innerHTML).toEqual('Who put the bomp in the bomp bah bomp bah bomp'); - expect(textNodes[0][1].innerHTML).toEqual('1'); - expect(textNodes[1][0].innerHTML).toEqual('Wh'); - expect(textNodes[1][1].innerHTML).toEqual('2.1'); - expect(textNodes[2][0].innerHTML).toEqual('Who put...'); - expect(textNodes[2][1].innerHTML).toEqual('3'); + assertHoverLabelContent({ + // There should be three points being hovered over, in three different traces, + // one in each plot. + nums: ['1', '2.1', '3'], + name: ['Who put the bomp in the bomp bah bomp bah bomp', 'Wh', 'Who put...'], + // There should be a single label on the y-axis with the shared y value, 0. + axis: '0' + }); }); }); }); @@ -776,27 +749,17 @@ describe('hover info on overlaid subplots', function() { Plotly.plot(createGraphDiv(), mock.data, mock.layout).then(function() { mouseEvent('mousemove', 768, 345); - var axisText = d3.selectAll('g.axistext'), - hoverText = d3.selectAll('g.hovertext'); - - expect(axisText.size()).toEqual(1, 'with 1 label on axis'); - expect(hoverText.size()).toEqual(2, 'with 2 labels on the overlaid pts'); - - expect(axisText.select('text').html()).toEqual('1', 'with correct axis label'); - - var textNodes = hoverText.selectAll('text'); - - expect(textNodes[0][0].innerHTML).toEqual('Take Rate', 'with correct hover labels'); - expect(textNodes[0][1].innerHTML).toEqual('0.35', 'with correct hover labels'); - expect(textNodes[1][0].innerHTML).toEqual('Revenue', 'with correct hover labels'); - expect(textNodes[1][1].innerHTML).toEqual('2,352.5', 'with correct hover labels'); - - }).then(done); + assertHoverLabelContent({ + nums: ['0.35', '2,352.5'], + name: ['Take Rate', 'Revenue'], + axis: '1' + }); + }) + .then(done); }); }); describe('hover after resizing', function() { - 'use strict'; var gd; afterEach(destroyGraphDiv); @@ -811,51 +774,52 @@ describe('hover after resizing', function() { }); } - function assertLabelCount(pos, cnt, msg) { + function check(pos, expectation, msg) { Lib.clearThrottle(); mouseEvent('mousemove', pos[0], pos[1]); - - var hoverText = d3.selectAll('g.hovertext'); - expect(hoverText.size()).toBe(cnt, msg); + assertHoverLabelContent({ + nums: expectation[0], + name: expectation[1], + axis: expectation[2] + }, msg); } it('should work', function(done) { - var data = [{ y: [2, 1, 2] }], - layout = { width: 600, height: 500 }; gd = createGraphDiv(); - var pos0 = [305, 403], - pos1 = [401, 122]; + var data = [{ y: [2, 1, 2] }]; + var layout = { width: 600, height: 500 }; - Plotly.plot(gd, data, layout).then(function() { + var pos0 = [305, 403]; + var pos1 = [401, 122]; + Plotly.plot(gd, data, layout).then(function() { // to test https://github.com/plotly/plotly.js/issues/1044 - return _click(pos0); }) .then(function() { - return assertLabelCount(pos0, 1, 'before resize, showing pt label'); + return check(pos0, ['1', null, '1'], 'before resize, showing pt label'); }) .then(function() { - return assertLabelCount(pos1, 0, 'before resize, not showing blank spot'); + return check(pos1, [null, null, null], 'before resize, not showing blank spot'); }) .then(function() { return Plotly.relayout(gd, 'width', 500); }) .then(function() { - return assertLabelCount(pos0, 0, 'after resize, not showing blank spot'); + return check(pos0, [null, null, null], 'after resize, not showing blank spot'); }) .then(function() { - return assertLabelCount(pos1, 1, 'after resize, showing pt label'); + return check(pos1, ['2', null, '2'], 'after resize, showing pt label'); }) .then(function() { return Plotly.relayout(gd, 'width', 600); }) .then(function() { - return assertLabelCount(pos0, 1, 'back to initial, showing pt label'); + return check(pos0, ['1', null, '1'], 'back to initial, showing pt label'); }) .then(function() { - return assertLabelCount(pos1, 0, 'back to initial, not showing blank spot'); + return check(pos1, [null, null, null], 'back to initial, not showing blank spot'); }) .then(done); }); diff --git a/test/jasmine/tests/mapbox_test.js b/test/jasmine/tests/mapbox_test.js index 1075b0574ce..ae7ec7a9cf3 100644 --- a/test/jasmine/tests/mapbox_test.js +++ b/test/jasmine/tests/mapbox_test.js @@ -11,6 +11,10 @@ var destroyGraphDiv = require('../assets/destroy_graph_div'); var mouseEvent = require('../assets/mouse_event'); var failTest = require('../assets/fail_test'); +var customAssertions = require('../assets/custom_assertions'); +var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle; +var assertHoverLabelContent = customAssertions.assertHoverLabelContent; + var MAPBOX_ACCESS_TOKEN = require('@build/credentials.json').MAPBOX_ACCESS_TOKEN; var TRANSITION_DELAY = 500; var MOUSE_DELAY = 100; @@ -724,11 +728,17 @@ describe('@noCI, mapbox plots', function() { return assertMouseMove(pointPos, 1); }) .then(function() { - var path = d3.select('g.hovertext').select('path').node(); - var text = d3.select('g.hovertext').select('text.nums').node(); - - expect(path.style.fill).toEqual('rgb(255, 255, 0)', 'bgcolor'); - expect(text.style.fontSize).toEqual('20px', 'font.size[0]'); + assertHoverLabelStyle(d3.select('g.hovertext'), { + bgcolor: 'rgb(255, 255, 0)', + bordercolor: 'rgb(68, 68, 68)', + fontSize: 20, + fontFamily: 'Arial', + fontColor: 'rgb(68, 68, 68)' + }); + assertHoverLabelContent({ + nums: '(10°, 10°)', + name: 'trace 0' + }); }) .catch(failTest) .then(done); diff --git a/test/jasmine/tests/pie_test.js b/test/jasmine/tests/pie_test.js index 0e138d1fc56..4a9f675cf72 100644 --- a/test/jasmine/tests/pie_test.js +++ b/test/jasmine/tests/pie_test.js @@ -8,7 +8,10 @@ var failTest = require('../assets/fail_test'); var click = require('../assets/click'); var getClientPosition = require('../assets/get_client_position'); var mouseEvent = require('../assets/mouse_event'); -var assertHoverLabelStyle = require('../assets/custom_assertions').assertHoverLabelStyle; + +var customAssertions = require('../assets/custom_assertions'); +var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle; +var assertHoverLabelContent = customAssertions.assertHoverLabelContent; describe('Pie traces:', function() { 'use strict'; @@ -245,17 +248,10 @@ describe('pie hovering', function() { } function assertLabel(content, style, msg) { - var g = d3.selectAll('.hovertext'); - var lines = g.selectAll('.nums .line'); - - expect(lines.size()).toBe(content.length); - - lines.each(function(_, i) { - expect(d3.select(this).text()).toBe(content[i]); - }); + assertHoverLabelContent({nums: content}, msg); if(style) { - assertHoverLabelStyle(g, { + assertHoverLabelStyle(d3.select('.hovertext'), { bgcolor: style[0], bordercolor: style[1], fontSize: style[2], @@ -270,7 +266,7 @@ describe('pie hovering', function() { .then(_hover) .then(function() { assertLabel( - ['4', '5', '33.3%'], + ['4', '5', '33.3%'].join('\n'), ['rgb(31, 119, 180)', 'rgb(255, 255, 255)', 13, 'Arial', 'rgb(255, 255, 255)'], 'initial' ); @@ -279,7 +275,11 @@ describe('pie hovering', function() { }) .then(_hover) .then(function() { - assertLabel(['4', 'E', '5', '33.3%'], null, 'added text'); + assertLabel( + ['4', 'E', '5', '33.3%'].join('\n'), + null, + 'added text' + ); return Plotly.restyle(gd, 'hovertext', [[ 'Apple', 'Banana', 'Clementine', 'Dragon Fruit', 'Eggplant' @@ -287,13 +287,21 @@ describe('pie hovering', function() { }) .then(_hover) .then(function() { - assertLabel(['4', 'Eggplant', '5', '33.3%'], null, 'added hovertext'); + assertLabel( + ['4', 'Eggplant', '5', '33.3%'].join('\n'), + null, + 'added hovertext' + ); return Plotly.restyle(gd, 'hovertext', 'SUP'); }) .then(_hover) .then(function() { - assertLabel(['4', 'SUP', '5', '33.3%'], null, 'constant hovertext'); + assertLabel( + ['4', 'SUP', '5', '33.3%'].join('\n'), + null, + 'constant hovertext' + ); return Plotly.restyle(gd, { 'hoverlabel.bgcolor': [['red', 'green', 'blue', 'yellow', 'red']], @@ -306,7 +314,7 @@ describe('pie hovering', function() { .then(_hover) .then(function() { assertLabel( - ['4', 'SUP', '5', '33.3%'], + ['4', 'SUP', '5', '33.3%'].join('\n'), ['rgb(255, 0, 0)', 'rgb(255, 255, 0)', 15, 'Roboto', 'rgb(0, 0, 255)'], 'new styles' ); @@ -315,13 +323,17 @@ describe('pie hovering', function() { }) .then(_hover) .then(function() { - assertLabel(['4', '33.3%'], null, 'new hoverinfo'); + assertLabel(['4', '33.3%'].join('\n'), null, 'new hoverinfo'); return Plotly.restyle(gd, 'hoverinfo', [[null, null, null, null, 'dont+know+what+im-doing']]); }) .then(_hover) .then(function() { - assertLabel(['4', 'SUP', '5', '33.3%'], null, 'garbage hoverinfo'); + assertLabel( + ['4', 'SUP', '5', '33.3%'].join('\n'), + null, + 'garbage hoverinfo' + ); }) .catch(fail) .then(done); @@ -335,7 +347,7 @@ describe('pie hovering', function() { Plotly.plot(gd, mockCopy.data, mockCopy.layout) .then(_hover) .then(function() { - assertLabel(['0', '12|345|678@91', '99@9%']); + assertLabel('0\n12|345|678@91\n99@9%'); }) .then(done); }); diff --git a/test/jasmine/tests/scattergeo_test.js b/test/jasmine/tests/scattergeo_test.js index 95b62af55d5..e8ab62a8a87 100644 --- a/test/jasmine/tests/scattergeo_test.js +++ b/test/jasmine/tests/scattergeo_test.js @@ -10,6 +10,10 @@ var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); var mouseEvent = require('../assets/mouse_event'); +var customAssertions = require('../assets/custom_assertions'); +var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle; +var assertHoverLabelContent = customAssertions.assertHoverLabelContent; + describe('Test scattergeo defaults', function() { var traceIn, traceOut; @@ -233,10 +237,6 @@ describe('Test scattergeo calc', function() { describe('Test scattergeo hover', function() { var gd; - // we can't mock ScatterGeo.hoverPoints - // because geo hover relies on mouse event - // to set the c2p conversion functions - beforeEach(function(done) { gd = createGraphDiv(); @@ -251,39 +251,79 @@ describe('Test scattergeo hover', function() { afterEach(destroyGraphDiv); - function assertHoverLabels(expected) { - var hoverText = d3.selectAll('g.hovertext').selectAll('tspan'); + function check(pos, content, style) { + mouseEvent('mousemove', pos[0], pos[1]); - hoverText.each(function(_, i) { - expect(this.innerHTML).toEqual(expected[i]); + style = style || { + bgcolor: 'rgb(31, 119, 180)', + bordercolor: 'rgb(255, 255, 255)', + fontColor: 'rgb(255, 255, 255)', + fontSize: 13, + fontFamily: 'Arial' + }; + + assertHoverLabelContent({ + nums: content[0], + name: content[1] }); + assertHoverLabelStyle( + d3.select('g.hovertext'), + style + ); } it('should generate hover label info (base case)', function() { - mouseEvent('mousemove', 381, 221); - assertHoverLabels(['(10°, 10°)', 'A']); + check([381, 221], ['(10°, 10°)\nA', null]); + }); + + it('should generate hover label info (with trace name)', function(done) { + Plotly.restyle(gd, 'hoverinfo', 'lon+lat+text+name').then(function() { + check([381, 221], ['(10°, 10°)\nA', 'trace 0']); + }) + .then(done); }); it('should generate hover label info (\'text\' single value case)', function(done) { Plotly.restyle(gd, 'text', 'text').then(function() { - mouseEvent('mousemove', 381, 221); - assertHoverLabels(['(10°, 10°)', 'text']); + check([381, 221], ['(10°, 10°)\ntext', null]); }) .then(done); }); it('should generate hover label info (\'hovertext\' single value case)', function(done) { Plotly.restyle(gd, 'hovertext', 'hovertext').then(function() { - mouseEvent('mousemove', 381, 221); - assertHoverLabels(['(10°, 10°)', 'hovertext']); + check([381, 221], ['(10°, 10°)\nhovertext', null]); }) .then(done); }); it('should generate hover label info (\'hovertext\' array case)', function(done) { Plotly.restyle(gd, 'hovertext', ['Apple', 'Banana', 'Orange']).then(function() { - mouseEvent('mousemove', 381, 221); - assertHoverLabels(['(10°, 10°)', 'Apple']); + check([381, 221], ['(10°, 10°)\nApple', null]); + }) + .then(done); + }); + + it('should generate hover label with custom styling', function(done) { + Plotly.restyle(gd, { + 'hoverlabel.bgcolor': 'red', + 'hoverlabel.bordercolor': [['blue', 'black', 'green']] + }) + .then(function() { + check([381, 221], ['(10°, 10°)\nA', null], { + bgcolor: 'rgb(255, 0, 0)', + bordercolor: 'rgb(0, 0, 255)', + fontColor: 'rgb(0, 0, 255)', + fontSize: 13, + fontFamily: 'Arial' + }); + }) + .then(done); + }); + + it('should generate hover label with arrayOk \'hoverinfo\' settings', function(done) { + Plotly.restyle(gd, 'hoverinfo', [['lon', null, 'lat+name']]).then(function() { + check([381, 221], ['lon: 10°', null]); }) .then(done); }); diff --git a/test/jasmine/tests/scattermapbox_test.js b/test/jasmine/tests/scattermapbox_test.js index 931f946326c..3c8b98e12d0 100644 --- a/test/jasmine/tests/scattermapbox_test.js +++ b/test/jasmine/tests/scattermapbox_test.js @@ -7,6 +7,7 @@ var convert = require('@src/traces/scattermapbox/convert'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); +var fail = require('../assets/fail_test'); var mouseEvent = require('../assets/mouse_event'); var click = require('../assets/click'); @@ -604,6 +605,31 @@ describe('@noCI scattermapbox hover', function() { }) .then(done); }); + + it('should generate hover label (\'hoverinfo\' array case)', function(done) { + function check(expected) { + var out = hoverPoints(getPointData(gd), 11, 11)[0]; + expect(out.extraText).toEqual(expected); + } + + Plotly.restyle(gd, 'hoverinfo', [['lon', 'lat', 'lon+lat+name']]).then(function() { + check('lon: 10°'); + return Plotly.restyle(gd, 'hoverinfo', [['lat', 'lon', 'name']]); + }) + .then(function() { + check('lat: 10°'); + return Plotly.restyle(gd, 'hoverinfo', [['text', 'lon', 'name']]); + }) + .then(function() { + check('Apple'); + return Plotly.restyle(gd, 'hoverinfo', [[null, 'lon', 'name']]); + }) + .then(function() { + check('(10°, 10°)
Apple'); + }) + .catch(fail) + .then(done); + }); }); describe('@noCI Test plotly events on a scattermapbox plot:', function() { diff --git a/test/jasmine/tests/ternary_test.js b/test/jasmine/tests/ternary_test.js index 387e582a70c..45a4ee75c86 100644 --- a/test/jasmine/tests/ternary_test.js +++ b/test/jasmine/tests/ternary_test.js @@ -12,6 +12,10 @@ var click = require('../assets/click'); var doubleClick = require('../assets/double_click'); var getClientPosition = require('../assets/get_client_position'); +var customAssertions = require('../assets/custom_assertions'); +var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle; +var assertHoverLabelContent = customAssertions.assertHoverLabelContent; + describe('ternary plots', function() { 'use strict'; @@ -102,35 +106,58 @@ describe('ternary plots', function() { }); it('should display to hover labels', function(done) { - var hoverLabels; - mouseEvent('mousemove', blankPos[0], blankPos[1]); - hoverLabels = findHoverLabels(); - expect(hoverLabels.size()).toEqual(0, 'only on data points'); + assertHoverLabelContent([null, null], 'only on data points'); - mouseEvent('mousemove', pointPos[0], pointPos[1]); - hoverLabels = findHoverLabels(); - expect(hoverLabels.size()).toEqual(1, 'one per data point'); + function check(content, style, msg) { + Lib.clearThrottle(); + mouseEvent('mousemove', pointPos[0], pointPos[1]); + + assertHoverLabelContent({nums: content}, msg); + assertHoverLabelStyle(d3.select('g.hovertext'), style, msg); + } - var rows = hoverLabels.selectAll('tspan'); - expect(rows[0][0].innerHTML).toEqual('Component A: 0.5', 'with correct text'); - expect(rows[0][1].innerHTML).toEqual('B: 0.25', 'with correct text'); - expect(rows[0][2].innerHTML).toEqual('Component C: 0.25', 'with correct text'); + check([ + 'Component A: 0.5', + 'B: 0.25', + 'Component C: 0.25' + ].join('\n'), { + bgcolor: 'rgb(31, 119, 180)', + bordercolor: 'rgb(255, 255, 255)', + fontColor: 'rgb(255, 255, 255)', + fontSize: 13, + fontFamily: 'Arial' + }, 'one label per data pt'); Plotly.restyle(gd, { 'hoverlabel.bordercolor': 'blue', 'hoverlabel.font.family': [['Gravitas', 'Arial', 'Roboto']] }) .then(function() { - Lib.clearThrottle(); - mouseEvent('mousemove', pointPos[0], pointPos[1]); - - var pathStyle = window.getComputedStyle(d3.select('g.hovertext path').node()); - var textStyle = window.getComputedStyle(d3.select('g.hovertext text.nums').node()); - - expect(pathStyle.stroke).toEqual('rgb(0, 0, 255)', 'bordercolor'); - expect(textStyle.fontFamily).toEqual('Gravitas', 'font.family[0]'); + check([ + 'Component A: 0.5', + 'B: 0.25', + 'Component C: 0.25' + ].join('\n'), { + bgcolor: 'rgb(31, 119, 180)', + bordercolor: 'rgb(0, 0, 255)', + fontColor: 'rgb(0, 0, 255)', + fontSize: 13, + fontFamily: 'Gravitas' + }, 'after hoverlabel styling restyle call'); + + return Plotly.restyle(gd, 'hoverinfo', [['a', 'b+c', 'b']]); }) + .then(function() { + check('Component A: 0.5', { + bgcolor: 'rgb(31, 119, 180)', + bordercolor: 'rgb(0, 0, 255)', + fontColor: 'rgb(0, 0, 255)', + fontSize: 13, + fontFamily: 'Gravitas' + }, 'after hoverlabel styling restyle call'); + }) + .catch(fail) .then(done); }); @@ -319,10 +346,6 @@ describe('ternary plots', function() { return d3.selectAll('.ternary').selectAll('g.trace.' + type).size(); } - function findHoverLabels() { - return d3.select('.hoverlayer').selectAll('g'); - } - function drag(path) { var len = path.length;