diff --git a/src/plots/mapbox/convert_text_opts.js b/src/plots/mapbox/convert_text_opts.js new file mode 100644 index 00000000000..6ea827651ba --- /dev/null +++ b/src/plots/mapbox/convert_text_opts.js @@ -0,0 +1,72 @@ +/** +* Copyright 2012-2016, 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'); + + +/** + * Convert plotly.js 'textposition' to mapbox-gl 'anchor' and 'offset' + * (with the help of the icon size). + * + * @param {string} textpostion : plotly.js textposition value + * @param {number} iconSize : plotly.js icon size (e.g. marker.size for traces) + * + * @return {object} + * - anchor + * - offset + */ +module.exports = function convertTextOpts(textposition, iconSize) { + var parts = textposition.split(' '), + vPos = parts[0], + hPos = parts[1]; + + // ballpack values + var factor = Array.isArray(iconSize) ? Lib.mean(iconSize) : iconSize, + xInc = 0.5 + (factor / 100), + yInc = 1.5 + (factor / 100); + + var anchorVals = ['', ''], + offset = [0, 0]; + + switch(vPos) { + case 'top': + anchorVals[0] = 'top'; + offset[1] = -yInc; + break; + case 'bottom': + anchorVals[0] = 'bottom'; + offset[1] = yInc; + break; + } + + switch(hPos) { + case 'left': + anchorVals[1] = 'right'; + offset[0] = -xInc; + break; + case 'right': + anchorVals[1] = 'left'; + offset[0] = xInc; + break; + } + + // Mapbox text-anchor must be one of: + // center, left, right, top, bottom, + // top-left, top-right, bottom-left, bottom-right + + var anchor; + if(anchorVals[0] && anchorVals[1]) anchor = anchorVals.join('-'); + else if(anchorVals[0]) anchor = anchorVals[0]; + else if(anchorVals[1]) anchor = anchorVals[1]; + else anchor = 'center'; + + return { anchor: anchor, offset: offset }; +}; diff --git a/src/plots/mapbox/layers.js b/src/plots/mapbox/layers.js index 64b95d6b966..cc42528951a 100644 --- a/src/plots/mapbox/layers.js +++ b/src/plots/mapbox/layers.js @@ -10,6 +10,7 @@ 'use strict'; var Lib = require('../../lib'); +var convertTextOpts = require('./convert_text_opts'); function MapboxLayer(mapbox, index) { @@ -45,9 +46,15 @@ proto.update = function update(opts) { }; proto.needsNewSource = function(opts) { + + // for some reason changing layer to 'fill' or 'symbol' + // w/o changing the source throws an exception in mapbox-gl 0.18 ; + // stay safe and make new source on type changes + return ( this.sourceType !== opts.sourcetype || - this.source !== opts.source + this.source !== opts.source || + this.layerType !== opts.type ); }; @@ -95,10 +102,11 @@ proto.updateLayer = function(opts) { }; proto.updateStyle = function(opts) { - var paintOpts = convertPaintOpts(opts); + var convertedOpts = convertOpts(opts); if(isVisible(opts)) { - this.mapbox.setOptions(this.idLayer, 'setPaintProperty', paintOpts); + this.mapbox.setOptions(this.idLayer, 'setLayoutProperty', convertedOpts.layout); + this.mapbox.setOptions(this.idLayer, 'setPaintProperty', convertedOpts.paint); } }; @@ -121,31 +129,64 @@ function isVisible(opts) { ); } -function convertPaintOpts(opts) { - var paintOpts = {}; +function convertOpts(opts) { + var layout = {}, + paint = {}; switch(opts.type) { + case 'circle': + Lib.extendFlat(paint, { + 'circle-radius': opts.circle.radius, + 'circle-color': opts.color, + 'circle-opacity': opts.opacity + }); + break; + case 'line': - Lib.extendFlat(paintOpts, { + Lib.extendFlat(paint, { 'line-width': opts.line.width, - 'line-color': opts.line.color, + 'line-color': opts.color, 'line-opacity': opts.opacity }); break; case 'fill': - Lib.extendFlat(paintOpts, { - 'fill-color': opts.fillcolor, - 'fill-outline-color': opts.line.color, + Lib.extendFlat(paint, { + 'fill-color': opts.color, + 'fill-outline-color': opts.fill.outlinecolor, 'fill-opacity': opts.opacity - // no way to pass line.width at the moment + // no way to pass specify outline width at the moment + }); + break; + + case 'symbol': + var symbol = opts.symbol, + textOpts = convertTextOpts(symbol.textposition, symbol.iconsize); + + Lib.extendFlat(layout, { + 'icon-image': symbol.icon + '-15', + 'icon-size': symbol.iconsize / 10, + + 'text-field': symbol.text, + 'text-size': symbol.textfont.size, + 'text-anchor': textOpts.anchor, + 'text-offset': textOpts.offset + + // TODO font family + //'text-font': symbol.textfont.family.split(', '), + }); + + Lib.extendFlat(paint, { + 'icon-color': opts.color, + 'text-color': symbol.textfont.color, + 'text-opacity': opts.opacity }); break; } - return paintOpts; + return { layout: layout, paint: paint }; } function convertSourceOpts(opts) { diff --git a/src/plots/mapbox/layout_attributes.js b/src/plots/mapbox/layout_attributes.js index 9386335395b..f70fbfe027b 100644 --- a/src/plots/mapbox/layout_attributes.js +++ b/src/plots/mapbox/layout_attributes.js @@ -9,11 +9,10 @@ 'use strict'; -var scatterMapboxAttrs = require('../../traces/scattermapbox/attributes'); +var Lib = require('../../lib'); var defaultLine = require('../../components/color').defaultLine; -var extendFlat = require('../../lib').extendFlat; - -var lineAttrs = scatterMapboxAttrs.line; +var fontAttrs = require('../font_attributes'); +var textposition = require('../../traces/scatter/attributes').textposition; module.exports = { @@ -129,15 +128,18 @@ module.exports = { type: { valType: 'enumerated', - values: ['line', 'fill'], - dflt: 'line', + values: ['circle', 'line', 'fill', 'symbol'], + dflt: 'circle', role: 'info', description: [ 'Sets the layer type.', - 'Support for *raster*, *background* types is coming soon.' + 'Support for *raster*, *background* types is coming soon.', + 'Note that *line* and *fill* are not compatible with Point', + 'GeoJSON geometries.' ].join(' ') }, + // attributes shared between all types below: { valType: 'string', dflt: '', @@ -149,16 +151,18 @@ module.exports = { 'the layer will be inserted above every existing layer.' ].join(' ') }, - - line: { - color: extendFlat({}, lineAttrs.color, { - dflt: defaultLine - }), - width: lineAttrs.width + color: { + valType: 'color', + dflt: defaultLine, + role: 'style', + description: [ + 'Sets the primary layer color.', + 'If `type` is *circle*, color corresponds to the circle color', + 'If `type` is *line*, color corresponds to the line color', + 'If `type` is *fill*, color corresponds to the fill color', + 'If `type` is *symbol*, color corresponds to the icon color' + ].join(' ') }, - - fillcolor: scatterMapboxAttrs.fillcolor, - opacity: { valType: 'number', min: 0, @@ -166,6 +170,82 @@ module.exports = { dflt: 1, role: 'info', description: 'Sets the opacity of the layer.' + }, + + // type-specific style attributes + circle: { + radius: { + valType: 'number', + dflt: 15, + role: 'style', + description: [ + 'Sets the circle radius.', + 'Has an effect only when `type` is set to *circle*.' + ].join(' ') + } + }, + + line: { + width: { + valType: 'number', + dflt: 2, + role: 'style', + description: [ + 'Sets the line radius.', + 'Has an effect only when `type` is set to *line*.' + ].join(' ') + } + }, + + fill: { + outlinecolor: { + valType: 'color', + dflt: defaultLine, + role: 'style', + description: [ + 'Sets the fill outline color.', + 'Has an effect only when `type` is set to *fill*.' + ].join(' ') + } + }, + + symbol: { + icon: { + valType: 'string', + dflt: 'marker', + role: 'style', + description: [ + 'Sets the symbol icon image.', + 'Full list: https://www.mapbox.com/maki-icons/' + ].join(' ') + }, + iconsize: { + valType: 'number', + dflt: 10, + role: 'style', + description: [ + 'Sets the symbol icon size.', + 'Has an effect only when `type` is set to *symbol*.' + ].join(' ') + }, + text: { + valType: 'string', + dflt: '', + role: 'info', + description: [ + 'Sets the symbol text.' + ].join(' ') + }, + textfont: Lib.extendDeep({}, fontAttrs, { + description: [ + 'Sets the icon text font.', + 'Has an effect only when `type` is set to *symbol*.' + ].join(' '), + family: { + dflt: 'Open Sans Regular, Arial Unicode MS Regular' + } + }), + textposition: Lib.extendFlat({}, textposition, { arrayOk: false }) } } diff --git a/src/plots/mapbox/layout_defaults.js b/src/plots/mapbox/layout_defaults.js index 0a20ce81f45..92eac67f355 100644 --- a/src/plots/mapbox/layout_defaults.js +++ b/src/plots/mapbox/layout_defaults.js @@ -49,7 +49,7 @@ function handleLayerDefaults(containerIn, containerOut) { } for(var i = 0; i < layersIn.length; i++) { - layerIn = layersIn[i]; + layerIn = layersIn[i] || {}; layerOut = {}; var sourceType = coerce('sourcetype'); @@ -57,21 +57,33 @@ function handleLayerDefaults(containerIn, containerOut) { if(sourceType === 'vector') coerce('sourcelayer'); - // maybe add smart default based off 'fillcolor' ??? + // maybe add smart default based off GeoJSON geometry? var type = coerce('type'); - var lineColor; - if(type === 'line' || type === 'fill') { - lineColor = coerce('line.color'); + coerce('below'); + coerce('color'); + coerce('opacity'); + + if(type === 'circle') { + coerce('circle.radius'); } - // no way to pass line.width to fill layers - if(type === 'line') coerce('line.width'); + if(type === 'line') { + coerce('line.width'); + } - if(type === 'fill') coerce('fillcolor', lineColor); + if(type === 'fill') { + coerce('fill.outlinecolor'); + } - coerce('below'); - coerce('opacity'); + if(type === 'symbol') { + coerce('symbol.icon'); + coerce('symbol.iconsize'); + + coerce('symbol.text'); + Lib.coerceFont(coerce, 'symbol.textfont'); + coerce('symbol.textposition'); + } layersOut.push(layerOut); } diff --git a/src/plots/mapbox/mapbox.js b/src/plots/mapbox/mapbox.js index 7bd0150cc32..f1a71b60374 100644 --- a/src/plots/mapbox/mapbox.js +++ b/src/plots/mapbox/mapbox.js @@ -96,7 +96,8 @@ proto.createMap = function(calcData, fullLayout, resolve, reject) { }); // clear navigation container - var controlContainer = this.div.getElementsByClassName(constants.controlContainerClassName)[0]; + var className = constants.controlContainerClassName, + controlContainer = this.div.getElementsByClassName(className)[0]; this.div.removeChild(controlContainer); self.rejectOnError(reject); @@ -113,6 +114,8 @@ proto.createMap = function(calcData, fullLayout, resolve, reject) { var center = map.getCenter(); opts._input.center = opts.center = { lon: center.lng, lat: center.lat }; opts._input.zoom = opts.zoom = map.getZoom(); + opts._input.bearing = opts.bearing = map.getBearing(); + opts._input.pitch = opts.pitch = map.getPitch(); }); map.on('mousemove', function(evt) { diff --git a/src/traces/scattermapbox/attributes.js b/src/traces/scattermapbox/attributes.js index 38bb7d909e6..c66c259e809 100644 --- a/src/traces/scattermapbox/attributes.js +++ b/src/traces/scattermapbox/attributes.js @@ -10,6 +10,7 @@ var scatterGeoAttrs = require('../scattergeo/attributes'); var scatterAttrs = require('../scatter/attributes'); +var mapboxAttrs = require('../../plots/mapbox/layout_attributes'); var plotAttrs = require('../../plots/attributes'); var extendFlat = require('../../lib/extend').extendFlat; @@ -104,8 +105,8 @@ module.exports = { }, fillcolor: scatterAttrs.fillcolor, - textfont: extendFlat({}, scatterAttrs.textfont, { arrayOk: false }), - textposition: extendFlat({}, scatterAttrs.textposition, { arrayOk: false }), + textfont: mapboxAttrs.layers.symbol.textfont, + textposition: mapboxAttrs.layers.symbol.textposition, hoverinfo: extendFlat({}, plotAttrs.hoverinfo, { flags: ['lon', 'lat', 'text', 'name'] diff --git a/src/traces/scattermapbox/convert.js b/src/traces/scattermapbox/convert.js index b469acc61d9..f8d6d96c9e4 100644 --- a/src/traces/scattermapbox/convert.js +++ b/src/traces/scattermapbox/convert.js @@ -11,6 +11,7 @@ var Lib = require('../../lib'); var subTypes = require('../scatter/subtypes'); +var convertTextOpts = require('../../plots/mapbox/convert_text_opts'); var COLOR_PROP = 'circle-color'; var SIZE_PROP = 'circle-radius'; @@ -108,13 +109,16 @@ module.exports = function convert(calcTrace) { } if(hasText) { - var textOpts = calcTextOpts(trace); + var iconSize = (trace.marker || {}).size, + textOpts = convertTextOpts(trace.textposition, iconSize); Lib.extendFlat(symbol.layout, { - 'text-font': trace.textfont.textfont, 'text-size': trace.textfont.size, 'text-anchor': textOpts.anchor, 'text-offset': textOpts.offset + + // TODO font family + //'text-font': symbol.textfont.family.split(', '), }); Lib.extendFlat(symbol.paint, { @@ -307,56 +311,6 @@ function calcCircleRadius(trace, hash) { return out; } -function calcTextOpts(trace) { - var textposition = trace.textposition, - parts = textposition.split(' '), - vPos = parts[0], - hPos = parts[1]; - - // ballpack values - var ms = (trace.marker || {}).size, - factor = Array.isArray(ms) ? Lib.mean(ms) : ms, - xInc = 0.5 + (factor / 100), - yInc = 1.5 + (factor / 100); - - var anchorVals = ['', ''], - offset = [0, 0]; - - switch(vPos) { - case 'top': - anchorVals[0] = 'top'; - offset[1] = -yInc; - break; - case 'bottom': - anchorVals[0] = 'bottom'; - offset[1] = yInc; - break; - } - - switch(hPos) { - case 'left': - anchorVals[1] = 'right'; - offset[0] = -xInc; - break; - case 'right': - anchorVals[1] = 'left'; - offset[0] = xInc; - break; - } - - // Mapbox text-anchor must be one of: - // center, left, right, top, bottom, - // top-left, top-right, bottom-left, bottom-right - - var anchor; - if(anchorVals[0] && anchorVals[1]) anchor = anchorVals.join('-'); - else if(anchorVals[0]) anchor = anchorVals[0]; - else if(anchorVals[1]) anchor = anchorVals[1]; - else anchor = 'center'; - - return { anchor: anchor, offset: offset }; -} - function getCoords(calcTrace) { var trace = calcTrace[0].trace, connectgaps = trace.connectgaps; diff --git a/src/traces/scattermapbox/defaults.js b/src/traces/scattermapbox/defaults.js index 8368ddabede..aa9dfde2a53 100644 --- a/src/traces/scattermapbox/defaults.js +++ b/src/traces/scattermapbox/defaults.js @@ -45,7 +45,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('mode'); if(subTypes.hasLines(traceOut)) { - handleLineDefaults(traceIn, traceOut, defaultColor, coerce); + handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce); coerce('connectgaps'); } diff --git a/test/image/assets/get_image_paths.js b/test/image/assets/get_image_paths.js index 6883d261c83..915bce2c2c0 100644 --- a/test/image/assets/get_image_paths.js +++ b/test/image/assets/get_image_paths.js @@ -20,10 +20,10 @@ module.exports = function getImagePaths(mockName, format) { return { baseline: join(constants.pathToTestImageBaselines, mockName, format), test: join(constants.pathToTestImages, mockName, format), - diff: join(constants.pathToTestImagesDiff, mockName, format) + diff: join(constants.pathToTestImagesDiff, 'diff-' + mockName, format) }; }; -function join(basePath, mockName, format) { - return path.join(basePath, mockName) + '.' + format; +function join(basePath, fileName, format) { + return path.join(basePath, fileName) + '.' + format; } diff --git a/test/image/mocks/mapbox_layers.json b/test/image/mocks/mapbox_layers.json index 9d98da7dcc8..9edb823de66 100644 --- a/test/image/mocks/mapbox_layers.json +++ b/test/image/mocks/mapbox_layers.json @@ -532,16 +532,17 @@ }, "type": "fill", "below": "water", - "fillcolor": "#ece2f0", + "color": "#ece2f0", "opacity": 0.8 }, { "sourcetype": "vector", "source": "mapbox://mapbox.mapbox-terrain-v2", "sourcelayer": "contour", + "type": "line", + "color": "red", "line": { - "width": 2, - "color": "red" + "width": 2 } } ] diff --git a/test/jasmine/assets/has_webgl_support.js b/test/jasmine/assets/has_webgl_support.js new file mode 100644 index 00000000000..dcf9f2ec3d1 --- /dev/null +++ b/test/jasmine/assets/has_webgl_support.js @@ -0,0 +1,22 @@ +'use strict'; + + +module.exports = function hasWebGLSupport(testName) { + var gl, canvas; + + try { + canvas = document.createElement('canvas'); + gl = canvas.getContext('webgl'); + } + catch(err) { + gl = null; + } + + var hasSupport = !!gl; + + if(!hasSupport) { + console.warn('Cannot get WebGL context. Skip test *' + testName + '*'); + } + + return hasSupport; +}; diff --git a/test/jasmine/karma.ciconf.js b/test/jasmine/karma.ciconf.js index 38f63ccac97..e735ef639ad 100644 --- a/test/jasmine/karma.ciconf.js +++ b/test/jasmine/karma.ciconf.js @@ -17,9 +17,7 @@ function func(config) { func.defaultConfig.exclude = [ 'tests/gl_plot_interact_test.js', 'tests/gl_plot_interact_basic_test.js', - 'tests/gl2d_scatterplot_contour_test.js', - 'tests/mapbox_test.js', - 'tests/scattermapbox_test.js' + 'tests/gl2d_scatterplot_contour_test.js' ]; // if true, Karma captures browsers, runs the tests and exits diff --git a/test/jasmine/tests/mapbox_test.js b/test/jasmine/tests/mapbox_test.js index b335b861f95..b6184d991cb 100644 --- a/test/jasmine/tests/mapbox_test.js +++ b/test/jasmine/tests/mapbox_test.js @@ -7,6 +7,7 @@ var supplyLayoutDefaults = require('@src/plots/mapbox/layout_defaults'); var d3 = require('d3'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); +var hasWebGLSupport = require('../assets/has_webgl_support'); var mouseEvent = require('../assets/mouse_event'); var customMatchers = require('../assets/custom_matchers'); @@ -84,43 +85,69 @@ describe('mapbox defaults', function() { }); it('should only coerce relevant layer style attributes', function() { + var base = { + line: { width: 3 }, + fill: { outlinecolor: '#d3d3d3' }, + circle: { radius: 20 }, + symbol: { icon: 'monument' } + }; + layoutIn = { mapbox: { - layers: [{ - sourcetype: 'vector', - type: 'line', - line: { - color: 'red', - width: 3 - }, - fillcolor: 'blue' - }, { - sourcetype: 'geojson', - type: 'fill', - line: { - color: 'red', - width: 3 - }, - fillcolor: 'blue' - }] + layers: [ + Lib.extendFlat({}, base, { + type: 'line', + color: 'red' + }), + Lib.extendFlat({}, base, { + type: 'fill', + color: 'blue' + }), + Lib.extendFlat({}, base, { + type: 'circle', + color: 'green' + }), + Lib.extendFlat({}, base, { + type: 'symbol', + color: 'yellow' + }) + ] } }; supplyLayoutDefaults(layoutIn, layoutOut, fullData); - expect(layoutOut.mapbox.layers[0].line.color).toEqual('red'); + expect(layoutOut.mapbox.layers[0].color).toEqual('red'); expect(layoutOut.mapbox.layers[0].line.width).toEqual(3); - expect(layoutOut.mapbox.layers[0].fillcolor).toBeUndefined(); - - expect(layoutOut.mapbox.layers[1].line.color).toEqual('red'); - expect(layoutOut.mapbox.layers[1].line.width).toBeUndefined(); - expect(layoutOut.mapbox.layers[1].fillcolor).toEqual('blue'); + expect(layoutOut.mapbox.layers[0].fill).toBeUndefined(); + expect(layoutOut.mapbox.layers[0].circle).toBeUndefined(); + expect(layoutOut.mapbox.layers[0].symbol).toBeUndefined(); + + expect(layoutOut.mapbox.layers[1].color).toEqual('blue'); + expect(layoutOut.mapbox.layers[1].fill.outlinecolor).toEqual('#d3d3d3'); + expect(layoutOut.mapbox.layers[1].line).toBeUndefined(); + expect(layoutOut.mapbox.layers[1].circle).toBeUndefined(); + expect(layoutOut.mapbox.layers[1].symbol).toBeUndefined(); + + expect(layoutOut.mapbox.layers[2].color).toEqual('green'); + expect(layoutOut.mapbox.layers[2].circle.radius).toEqual(20); + expect(layoutOut.mapbox.layers[2].line).toBeUndefined(); + expect(layoutOut.mapbox.layers[2].fill).toBeUndefined(); + expect(layoutOut.mapbox.layers[2].symbol).toBeUndefined(); + + expect(layoutOut.mapbox.layers[3].color).toEqual('yellow'); + expect(layoutOut.mapbox.layers[3].symbol.icon).toEqual('monument'); + expect(layoutOut.mapbox.layers[3].line).toBeUndefined(); + expect(layoutOut.mapbox.layers[3].fill).toBeUndefined(); + expect(layoutOut.mapbox.layers[3].circle).toBeUndefined(); }); }); describe('mapbox credentials', function() { 'use strict'; + if(!hasWebGLSupport('scattermapbox hover')) return; + var dummyToken = 'asfdsa124331wersdsa1321q3'; var gd; @@ -168,6 +195,8 @@ describe('mapbox credentials', function() { describe('mapbox plots', function() { 'use strict'; + if(!hasWebGLSupport('scattermapbox hover')) return; + var mock = require('@mocks/mapbox_0.json'), gd; @@ -348,14 +377,14 @@ describe('mapbox plots', function() { }; var styleUpdate0 = { - 'mapbox.layers[0].fillcolor': 'red', - 'mapbox.layers[0].line.color': 'blue', + 'mapbox.layers[0].color': 'red', + 'mapbox.layers[0].fill.outlinecolor': 'blue', 'mapbox.layers[0].opacity': 0.3 }; var styleUpdate1 = { + 'mapbox.layers[1].color': 'blue', 'mapbox.layers[1].line.width': 3, - 'mapbox.layers[1].line.color': 'blue', 'mapbox.layers[1].opacity': 0.6 }; diff --git a/test/jasmine/tests/scattermapbox_test.js b/test/jasmine/tests/scattermapbox_test.js index 5aa47a1b385..7607d8f1739 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 hasWebGLSupport = require('../assets/has_webgl_support'); var customMatchers = require('../assets/custom_matchers'); // until it is part of the main plotly.js bundle @@ -410,6 +411,8 @@ describe('scattermapbox convert', function() { describe('scattermapbox hover', function() { 'use strict'; + if(!hasWebGLSupport('scattermapbox hover')) return; + var hoverPoints = ScatterMapbox.hoverPoints; var gd; @@ -456,7 +459,7 @@ describe('scattermapbox hover', function() { expect(out.index).toEqual(0); expect([out.x0, out.x1, out.y0, out.y1]).toBeCloseToArray([ - 444.444, 446.444, 105.410, 107.410 + 297.444, 299.444, 105.410, 107.410 ]); expect(out.extraText).toEqual('(10°, 10°)
A'); expect(out.color).toEqual('#1f77b4'); @@ -470,7 +473,7 @@ describe('scattermapbox hover', function() { expect(out.index).toEqual(0); expect([out.x0, out.x1, out.y0, out.y1]).toBeCloseToArray([ - 2492.444, 2494.444, 105.410, 107.410 + 2345.444, 2347.444, 105.410, 107.410 ]); expect(out.extraText).toEqual('(10°, 10°)
A'); expect(out.color).toEqual('#1f77b4'); @@ -484,7 +487,7 @@ describe('scattermapbox hover', function() { expect(out.index).toEqual(0); expect([out.x0, out.x1, out.y0, out.y1]).toBeCloseToArray([ - -2627.555, -2625.555, 105.410, 107.410 + -2774.555, -2772.555, 105.410, 107.410 ]); expect(out.extraText).toEqual('(10°, 10°)
A'); expect(out.color).toEqual('#1f77b4');