From 0b898e9bb3cf4a8e6bcdaad69fa37e8bfaf10885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Thu, 8 Jun 2017 16:54:54 -0400 Subject: [PATCH 1/8] append *all* values inside array attr in event data - data attributes (e.g. ids, customdata) and arrayOk ('marker.size', 'text') values of click/hover'ed points are now included in the event data (along side the pts' coords) - to do so, cache `findArrayAttributes` results in full trace object and use that cache in hover routine. --- src/components/fx/helpers.js | 24 ++++++++++++++++++++++++ src/components/fx/hover.js | 1 + src/plots/plots.js | 1 + 3 files changed, 26 insertions(+) diff --git a/src/components/fx/helpers.js b/src/components/fx/helpers.js index c45da2b0ee5..209770b74d2 100644 --- a/src/components/fx/helpers.js +++ b/src/components/fx/helpers.js @@ -8,6 +8,7 @@ 'use strict'; +var Lib = require('../../lib'); var constants = require('./constants'); // look for either subplot or xaxis and yaxis attributes @@ -83,3 +84,26 @@ function quadrature(dx, dy) { return Math.sqrt(x * x + y * y); }; } + +/** Appends values inside array attributes corresponding to given point number + * + * @param {object} pointData : point data object (gets mutated here + * @param {object} trace : full trace object + * @param {number} ptNumber : point number + */ +exports.appendArrayPointValue = function(pointData, trace, ptNumber) { + var arrayAttrs = trace._arrayAttrs; + + for(var i = 0; i < arrayAttrs.length; i++) { + var astr = arrayAttrs[i]; + var key; + + if(astr === 'ids') key = 'id'; + else if(astr === 'location') key = 'location'; + else key = astr; + + if(pointData[key] === undefined) { + pointData[key] = Lib.nestedProperty(trace, astr).get()[ptNumber]; + } + } +}; diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index b9ebbd59491..a0eca7726f9 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -457,6 +457,7 @@ function _hover(gd, evt, subplot) { if(pt.zLabelVal !== undefined) out.z = pt.zLabelVal; } + helpers.appendArrayPointValue(out, trace, pt.index); newhoverdata.push(out); } diff --git a/src/plots/plots.js b/src/plots/plots.js index ae79bcf7af5..484653df99c 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -676,6 +676,7 @@ plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) { Lib.pushUnique(modules, _module); Lib.pushUnique(basePlotModules, fullTrace._module.basePlotModule); + fullTrace._arrayAttrs = PlotSchema.findArrayAttributes(fullTrace); cnt++; } From f17367f1af73293cb6ed3e9ac5c817189e19d60b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Thu, 8 Jun 2017 16:55:25 -0400 Subject: [PATCH 2/8] implement appendArrayPointValue in gl3d and gl2d --- src/components/fx/index.js | 1 + src/plots/gl2d/scene2d.js | 27 +++++++++++++++------------ src/plots/gl3d/scene.js | 22 ++++++++++++---------- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/components/fx/index.js b/src/components/fx/index.js index bec718443c8..34d7f711855 100644 --- a/src/components/fx/index.js +++ b/src/components/fx/index.js @@ -35,6 +35,7 @@ module.exports = { getDistanceFunction: helpers.getDistanceFunction, getClosest: helpers.getClosest, inbox: helpers.inbox, + appendArrayPointValue: helpers.appendArrayPointValue, castHoverOption: castHoverOption, castHoverinfo: castHoverinfo, diff --git a/src/plots/gl2d/scene2d.js b/src/plots/gl2d/scene2d.js index 65e854f5e96..73faa3085d4 100644 --- a/src/plots/gl2d/scene2d.js +++ b/src/plots/gl2d/scene2d.js @@ -512,6 +512,7 @@ proto.updateTraces = function(fullData, calcData) { proto.emitPointAction = function(nextSelection, eventType) { var uid = nextSelection.trace.uid; + var ptNumber = nextSelection.pointIndex; var trace; for(var i = 0; i < this.fullData.length; i++) { @@ -520,18 +521,20 @@ proto.emitPointAction = function(nextSelection, eventType) { } } - this.graphDiv.emit(eventType, { - points: [{ - x: nextSelection.traceCoord[0], - y: nextSelection.traceCoord[1], - curveNumber: trace.index, - pointNumber: nextSelection.pointIndex, - data: trace._input, - fullData: this.fullData, - xaxis: this.xaxis, - yaxis: this.yaxis - }] - }); + var pointData = { + x: nextSelection.traceCoord[0], + y: nextSelection.traceCoord[1], + curveNumber: trace.index, + pointNumber: ptNumber, + data: trace._input, + fullData: this.fullData, + xaxis: this.xaxis, + yaxis: this.yaxis + }; + + Fx.appendArrayPointValue(pointData, trace, ptNumber); + + this.graphDiv.emit(eventType, {points: [pointData]}); }; proto.draw = function() { diff --git a/src/plots/gl3d/scene.js b/src/plots/gl3d/scene.js index db6c2e7dbbb..be01cc86a8c 100644 --- a/src/plots/gl3d/scene.js +++ b/src/plots/gl3d/scene.js @@ -102,18 +102,20 @@ function render(scene) { }); } - var eventData = { - points: [{ - x: selection.traceCoordinate[0], - y: selection.traceCoordinate[1], - z: selection.traceCoordinate[2], - data: trace._input, - fullData: trace, - curveNumber: trace.index, - pointNumber: ptNumber - }] + var pointData = { + x: selection.traceCoordinate[0], + y: selection.traceCoordinate[1], + z: selection.traceCoordinate[2], + data: trace._input, + fullData: trace, + curveNumber: trace.index, + pointNumber: ptNumber }; + Fx.appendArrayPointValue(pointData); + + var eventData = {points: [pointData]}; + if(selection.buttons && selection.distance < 5) { scene.graphDiv.emit('plotly_click', eventData); } From 1c2e71e36c1b5fcefea05cac9c684dcaa88436ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Thu, 8 Jun 2017 16:55:43 -0400 Subject: [PATCH 3/8] add a few usefull TODOs --- src/components/fx/hover.js | 2 ++ src/plots/plots.js | 2 ++ src/traces/scatter/select.js | 1 + 3 files changed, 5 insertions(+) diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index a0eca7726f9..1bf7ac2dab4 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -447,10 +447,12 @@ function _hover(gd, evt, subplot) { pointNumber: pt.index }; + // could maybe :hocho: _module.eventData if(pt.trace._module.eventData) out = pt.trace._module.eventData(out, pt); else { out.x = pt.xVal; out.y = pt.yVal; + out.xaxis = pt.xa; out.yaxis = pt.ya; diff --git a/src/plots/plots.js b/src/plots/plots.js index 484653df99c..111f61e9362 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -676,7 +676,9 @@ plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) { Lib.pushUnique(modules, _module); Lib.pushUnique(basePlotModules, fullTrace._module.basePlotModule); + // TODO remove findArrayAttributes calls downstream fullTrace._arrayAttrs = PlotSchema.findArrayAttributes(fullTrace); + cnt++; } diff --git a/src/traces/scatter/select.js b/src/traces/scatter/select.js index 222d2b22566..4681e6f3d91 100644 --- a/src/traces/scatter/select.js +++ b/src/traces/scatter/select.js @@ -47,6 +47,7 @@ module.exports = function selectPoints(searchInfo, polygon) { pointNumber: i, x: di.x, y: di.y, + // TODO generalize with hover/click data handler id: di.id }); di.dim = 0; From b831cbccfaacc42a944b2a1af0f34a64ac77e12f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 14 Jun 2017 15:26:20 -0400 Subject: [PATCH 4/8] misc. fixups for appendArrayPointValue --- src/components/fx/helpers.js | 13 ++++++++----- src/components/fx/hover.js | 4 +--- src/plot_api/plot_schema.js | 7 ++++--- src/plots/gl3d/scene.js | 2 +- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/components/fx/helpers.js b/src/components/fx/helpers.js index 209770b74d2..a2b5580d026 100644 --- a/src/components/fx/helpers.js +++ b/src/components/fx/helpers.js @@ -87,11 +87,11 @@ function quadrature(dx, dy) { /** Appends values inside array attributes corresponding to given point number * - * @param {object} pointData : point data object (gets mutated here + * @param {object} pointData : point data object (gets mutated here) * @param {object} trace : full trace object - * @param {number} ptNumber : point number + * @param {number} pointNumber : point number */ -exports.appendArrayPointValue = function(pointData, trace, ptNumber) { +exports.appendArrayPointValue = function(pointData, trace, pointNumber) { var arrayAttrs = trace._arrayAttrs; for(var i = 0; i < arrayAttrs.length; i++) { @@ -99,11 +99,14 @@ exports.appendArrayPointValue = function(pointData, trace, ptNumber) { var key; if(astr === 'ids') key = 'id'; - else if(astr === 'location') key = 'location'; + else if(astr === 'locations') key = 'location'; else key = astr; if(pointData[key] === undefined) { - pointData[key] = Lib.nestedProperty(trace, astr).get()[ptNumber]; + var val = Lib.nestedProperty(trace, astr).get(); + pointData[key] = Array.isArray(pointNumber) ? + val[pointNumber[0]][pointNumber[1]] : + val[pointNumber]; } } }; diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index 1bf7ac2dab4..46b0661d211 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -447,19 +447,17 @@ function _hover(gd, evt, subplot) { pointNumber: pt.index }; - // could maybe :hocho: _module.eventData if(pt.trace._module.eventData) out = pt.trace._module.eventData(out, pt); else { out.x = pt.xVal; out.y = pt.yVal; - out.xaxis = pt.xa; out.yaxis = pt.ya; if(pt.zLabelVal !== undefined) out.z = pt.zLabelVal; } - helpers.appendArrayPointValue(out, trace, pt.index); + helpers.appendArrayPointValue(out, pt.trace, pt.index); newhoverdata.push(out); } diff --git a/src/plot_api/plot_schema.js b/src/plot_api/plot_schema.js index 0c88857c714..03bc35ad760 100644 --- a/src/plot_api/plot_schema.js +++ b/src/plot_api/plot_schema.js @@ -157,7 +157,9 @@ exports.findArrayAttributes = function(trace) { } exports.crawl(baseAttributes, callback); - exports.crawl(trace._module.attributes, callback); + if(trace._module && trace._module.attributes) { + exports.crawl(trace._module.attributes, callback); + } if(trace.transforms) { var transforms = trace.transforms; @@ -177,9 +179,8 @@ exports.findArrayAttributes = function(trace) { // At the moment, we need this block to make sure that // ohlc and candlestick 'open', 'high', 'low', 'close' can be // used with filter ang groupby transforms. - if(trace._fullInput) { + if(trace._fullInput && trace._fullInput._module && trace._fullInput._module.attributes) { exports.crawl(trace._fullInput._module.attributes, callback); - arrayAttributes = Lib.filterUnique(arrayAttributes); } diff --git a/src/plots/gl3d/scene.js b/src/plots/gl3d/scene.js index be01cc86a8c..5bc4098ad05 100644 --- a/src/plots/gl3d/scene.js +++ b/src/plots/gl3d/scene.js @@ -112,7 +112,7 @@ function render(scene) { pointNumber: ptNumber }; - Fx.appendArrayPointValue(pointData); + Fx.appendArrayPointValue(pointData, trace, ptNumber); var eventData = {points: [pointData]}; From 129ad6c35f2b772b1f010351cbe9729d7600960a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 14 Jun 2017 15:28:07 -0400 Subject: [PATCH 5/8] make 'ids' and 'customdata' base trace attributes - making them valid for all trace types --- src/plots/attributes.js | 16 ++++++++++++++++ src/plots/plots.js | 3 +++ src/traces/scatter/attributes.js | 8 -------- src/traces/scatter/defaults.js | 2 -- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/plots/attributes.js b/src/plots/attributes.js index 43ce263b79d..405623be271 100644 --- a/src/plots/attributes.js +++ b/src/plots/attributes.js @@ -69,6 +69,22 @@ module.exports = { role: 'info', dflt: '' }, + ids: { + valType: 'data_array', + description: [ + 'Assigns id labels to each datum.', + 'These ids for object constancy of data points during animation.' + ].join(' ') + }, + customdata: { + valType: 'data_array', + description: [ + 'Assigns extra data each datum.', + 'This may be useful when listening to hover, click and selection events.', + 'Note that, *scatter* traces also appends customdata items in the markers', + 'DOM elements' + ].join(' ') + }, hoverinfo: { valType: 'flaglist', role: 'info', diff --git a/src/plots/plots.js b/src/plots/plots.js index 111f61e9362..31c5fe23a52 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -865,6 +865,9 @@ plots.supplyTraceDefaults = function(traceIn, traceOutIndex, layout, traceInInde } if(visible) { + coerce('customdata'); + coerce('ids'); + var _module = plots.getModule(traceOut); traceOut._module = _module; diff --git a/src/traces/scatter/attributes.js b/src/traces/scatter/attributes.js index 4d88511c280..726bb94bfef 100644 --- a/src/traces/scatter/attributes.js +++ b/src/traces/scatter/attributes.js @@ -57,10 +57,6 @@ module.exports = { 'where `y0` is the starting coordinate and `dy` the step.' ].join(' ') }, - customdata: { - valType: 'data_array', - description: 'Assigns extra data to each scatter point DOM element' - }, dy: { valType: 'number', dflt: 1, @@ -70,10 +66,6 @@ module.exports = { 'See `y0` for more info.' ].join(' ') }, - ids: { - valType: 'data_array', - description: 'A list of keys for object constancy of data points during animation' - }, text: { valType: 'string', role: 'info', diff --git a/src/traces/scatter/defaults.js b/src/traces/scatter/defaults.js index 5d0b9faf7c7..e7ed99fdda2 100644 --- a/src/traces/scatter/defaults.js +++ b/src/traces/scatter/defaults.js @@ -36,11 +36,9 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout return; } - coerce('customdata'); coerce('text'); coerce('hovertext'); coerce('mode', defaultMode); - coerce('ids'); if(subTypes.hasLines(traceOut)) { handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce); From ee1adf8f0f9aa67662093058e2e1c9b0b93a4d36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 14 Jun 2017 15:30:15 -0400 Subject: [PATCH 6/8] call PlotSchema.findArrayAttributes in doCalcdata - no need to call on every update, when data array change is sufficient. - note that the groupby transform still need to call Plots.findArrayAttributes on its own as its transform method is called during the defaults. --- src/plots/plots.js | 11 +++++++---- src/transforms/filter.js | 3 +-- src/transforms/sort.js | 3 +-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/plots/plots.js b/src/plots/plots.js index 31c5fe23a52..38416678886 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -521,7 +521,7 @@ plots.supplyDefaults = function(gd) { function remapTransformedArrays(cd0, newTrace) { var oldTrace = cd0.trace; - var arrayAttrs = PlotSchema.findArrayAttributes(oldTrace); + var arrayAttrs = oldTrace._arrayAttrs; var transformedArrayHash = {}; var i, astr; @@ -676,9 +676,6 @@ plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) { Lib.pushUnique(modules, _module); Lib.pushUnique(basePlotModules, fullTrace._module.basePlotModule); - // TODO remove findArrayAttributes calls downstream - fullTrace._arrayAttrs = PlotSchema.findArrayAttributes(fullTrace); - cnt++; } @@ -2038,6 +2035,12 @@ plots.doCalcdata = function(gd, traces) { } } + // find array attributes in trace + for(i = 0; i < fullData.length; i++) { + trace = fullData[i]; + trace._arrayAttrs = PlotSchema.findArrayAttributes(trace); + } + initCategories(axList); var hasCalcTransform = false; diff --git a/src/transforms/filter.js b/src/transforms/filter.js index 2a2a3182d2a..a9799c65818 100644 --- a/src/transforms/filter.js +++ b/src/transforms/filter.js @@ -10,7 +10,6 @@ var Lib = require('../lib'); var Registry = require('../registry'); -var PlotSchema = require('../plot_api/plot_schema'); var Axes = require('../plots/cartesian/axes'); var COMPARISON_OPS = ['=', '!=', '<', '>=', '>', '<=']; @@ -148,6 +147,7 @@ exports.calcTransform = function(gd, trace, opts) { var target = opts.target; var len = targetArray.length; var targetCalendar = opts.targetcalendar; + var arrayAttrs = trace._arrayAttrs; // even if you provide targetcalendar, if target is a string and there // is a calendar attribute matching target it will get used instead. @@ -158,7 +158,6 @@ exports.calcTransform = function(gd, trace, opts) { var d2c = Axes.getDataToCoordFunc(gd, trace, target, targetArray); var filterFunc = getFilterFunc(opts, d2c, targetCalendar); - var arrayAttrs = PlotSchema.findArrayAttributes(trace); var originalArrays = {}; function forAllAttrs(fn, index) { diff --git a/src/transforms/sort.js b/src/transforms/sort.js index 97fbec17b3e..41e34f1e8e9 100644 --- a/src/transforms/sort.js +++ b/src/transforms/sort.js @@ -9,7 +9,6 @@ 'use strict'; var Lib = require('../lib'); -var PlotSchema = require('../plot_api/plot_schema'); var Axes = require('../plots/cartesian/axes'); exports.moduleType = 'transform'; @@ -78,7 +77,7 @@ exports.calcTransform = function(gd, trace, opts) { var target = opts.target; var len = targetArray.length; - var arrayAttrs = PlotSchema.findArrayAttributes(trace); + var arrayAttrs = trace._arrayAttrs; var d2c = Axes.getDataToCoordFunc(gd, trace, target, targetArray); var indices = getIndices(opts, targetArray, d2c); From d9373aa16a1c3f49c60043b8d6cb4b2ce53513a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 14 Jun 2017 15:30:57 -0400 Subject: [PATCH 7/8] update tests for improved event data --- test/jasmine/tests/geo_test.js | 25 ++++++++++++++------ test/jasmine/tests/gl2d_click_test.js | 4 ++-- test/jasmine/tests/gl_plot_interact_test.js | 21 +++++++++++++---- test/jasmine/tests/scattermapbox_test.js | 16 ++++++------- test/jasmine/tests/ternary_test.js | 26 +++++++++++++++------ 5 files changed, 64 insertions(+), 28 deletions(-) diff --git a/test/jasmine/tests/geo_test.js b/test/jasmine/tests/geo_test.js index bccd6ae3425..cdd6c90e261 100644 --- a/test/jasmine/tests/geo_test.js +++ b/test/jasmine/tests/geo_test.js @@ -536,7 +536,7 @@ describe('Test geo interactions', function() { it('should contain the correct fields', function() { expect(Object.keys(ptData)).toEqual([ 'data', 'fullData', 'curveNumber', 'pointNumber', - 'lon', 'lat', 'location' + 'lon', 'lat', 'location', 'marker.size' ]); expect(cnt).toEqual(1); }); @@ -547,6 +547,7 @@ describe('Test geo interactions', function() { expect(ptData.location).toBe(null); expect(ptData.curveNumber).toEqual(0); expect(ptData.pointNumber).toEqual(0); + expect(ptData['marker.size']).toEqual(20); expect(cnt).toEqual(1); }); @@ -598,7 +599,7 @@ describe('Test geo interactions', function() { it('should contain the correct fields', function() { expect(Object.keys(ptData)).toEqual([ 'data', 'fullData', 'curveNumber', 'pointNumber', - 'lon', 'lat', 'location' + 'lon', 'lat', 'location', 'marker.size' ]); }); @@ -608,6 +609,7 @@ describe('Test geo interactions', function() { expect(ptData.location).toBe(null); expect(ptData.curveNumber).toEqual(0); expect(ptData.pointNumber).toEqual(0); + expect(ptData['marker.size']).toEqual(20); }); }); @@ -629,7 +631,7 @@ describe('Test geo interactions', function() { it('should contain the correct fields', function() { expect(Object.keys(ptData)).toEqual([ 'data', 'fullData', 'curveNumber', 'pointNumber', - 'lon', 'lat', 'location' + 'lon', 'lat', 'location', 'marker.size' ]); }); @@ -639,6 +641,7 @@ describe('Test geo interactions', function() { expect(ptData.location).toBe(null); expect(ptData.curveNumber).toEqual(0); expect(ptData.pointNumber).toEqual(0); + expect(ptData['marker.size']).toEqual(20); }); }); @@ -1100,7 +1103,7 @@ describe('Test event property of interactions on a geo plot:', function() { expect(Object.keys(pt)).toEqual([ 'data', 'fullData', 'curveNumber', 'pointNumber', 'lon', 'lat', - 'location' + 'location', 'text', 'marker.size' ]); expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); @@ -1110,6 +1113,8 @@ describe('Test event property of interactions on a geo plot:', function() { expect(pt.lon).toEqual(57.75, 'points[0].lon'); expect(pt.location).toEqual(57.75, 'points[0].location'); expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + expect(pt.text).toEqual(20, 'points[0].text'); + expect(pt['marker.size']).toEqual(20, 'points[0][\'marker.size\']'); expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); @@ -1146,7 +1151,7 @@ describe('Test event property of interactions on a geo plot:', function() { expect(Object.keys(pt)).toEqual([ 'data', 'fullData', 'curveNumber', 'pointNumber', 'lon', 'lat', - 'location' + 'location', 'text', 'marker.size' ]); expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); @@ -1156,6 +1161,8 @@ describe('Test event property of interactions on a geo plot:', function() { expect(pt.lon).toEqual(57.75, 'points[0].lon'); expect(pt.location).toEqual(57.75, 'points[0].location'); expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + expect(pt.text).toEqual(20, 'points[0].text'); + expect(pt['marker.size']).toEqual(20, 'points[0][\'marker.size\']'); expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); @@ -1185,7 +1192,7 @@ describe('Test event property of interactions on a geo plot:', function() { expect(Object.keys(pt)).toEqual([ 'data', 'fullData', 'curveNumber', 'pointNumber', 'lon', 'lat', - 'location' + 'location', 'text', 'marker.size' ]); expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); @@ -1195,6 +1202,8 @@ describe('Test event property of interactions on a geo plot:', function() { expect(pt.lon).toEqual(57.75, 'points[0].lon'); expect(pt.location).toEqual(57.75, 'points[0].location'); expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + expect(pt.text).toEqual(20, 'points[0].text'); + expect(pt['marker.size']).toEqual(20, 'points[0][\'marker.size\']'); expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); @@ -1219,7 +1228,7 @@ describe('Test event property of interactions on a geo plot:', function() { expect(Object.keys(pt)).toEqual([ 'data', 'fullData', 'curveNumber', 'pointNumber', 'lon', 'lat', - 'location' + 'location', 'text', 'marker.size' ]); expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); @@ -1229,6 +1238,8 @@ describe('Test event property of interactions on a geo plot:', function() { expect(pt.lon).toEqual(57.75, 'points[0].lon'); expect(pt.location).toEqual(57.75, 'points[0].location'); expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + expect(pt.text).toEqual(20, 'points[0].text'); + expect(pt['marker.size']).toEqual(20, 'points[0][\'marker.size\']'); expect(evt.clientX).toEqual(nearPos[0], 'event.clientX'); expect(evt.clientY).toEqual(nearPos[1], 'event.clientY'); diff --git a/test/jasmine/tests/gl2d_click_test.js b/test/jasmine/tests/gl2d_click_test.js index 95b7ee5f6f1..d3804b256c4 100644 --- a/test/jasmine/tests/gl2d_click_test.js +++ b/test/jasmine/tests/gl2d_click_test.js @@ -114,10 +114,10 @@ describe('Test hover and click interactions', function() { var pt = actual.points[0]; - expect(Object.keys(pt)).toEqual([ + expect(Object.keys(pt)).toEqual(jasmine.arrayContaining([ 'x', 'y', 'curveNumber', 'pointNumber', 'data', 'fullData', 'xaxis', 'yaxis' - ], 'event data keys'); + ]), 'event data keys'); expect(typeof pt.data.uid).toEqual('string', 'uid'); expect(pt.xaxis.domain.length).toEqual(2, 'xaxis'); diff --git a/test/jasmine/tests/gl_plot_interact_test.js b/test/jasmine/tests/gl_plot_interact_test.js index 18eb06b8358..0a7154386ec 100644 --- a/test/jasmine/tests/gl_plot_interact_test.js +++ b/test/jasmine/tests/gl_plot_interact_test.js @@ -72,17 +72,21 @@ describe('Test gl3d plots', function() { expect(text.style('fill')).toEqual(fontColor, 'font.color'); } - function assertEventData(x, y, z, curveNumber, pointNumber) { - expect(Object.keys(ptData)).toEqual([ + function assertEventData(x, y, z, curveNumber, pointNumber, extra) { + expect(Object.keys(ptData)).toEqual(jasmine.arrayContaining([ 'x', 'y', 'z', 'data', 'fullData', 'curveNumber', 'pointNumber' - ], 'correct hover data fields'); + ]), 'correct hover data fields'); expect(ptData.x).toEqual(x, 'x val'); expect(ptData.y).toEqual(y, 'y val'); expect(ptData.z).toEqual(z, 'z val'); expect(ptData.curveNumber).toEqual(curveNumber, 'curveNumber'); expect(ptData.pointNumber).toEqual(pointNumber, 'pointNumber'); + + Object.keys(extra || {}).forEach(function(k) { + expect(ptData[k]).toBe(extra[k], k + ' val'); + }); } beforeEach(function() { @@ -114,7 +118,12 @@ describe('Test gl3d plots', function() { .then(delay) .then(function() { assertHoverText('x: 140.72', 'y: −96.97', 'z: −96.97'); - assertEventData(140.72, -96.97, -96.97, 0, 2); + assertEventData(140.72, -96.97, -96.97, 0, 2, { + 'marker.symbol': 'cross', + 'marker.size': 30, + 'marker.color': 'orange', + 'marker.line.color': undefined + }); assertHoverLabelStyle('rgb(0, 0, 255)', 'rgb(255, 255, 255)', 13, 'Arial', 'rgb(255, 255, 255)'); return Plotly.restyle(gd, { @@ -239,6 +248,10 @@ describe('Test gl3d plots', function() { }) .then(_hover) .then(function() { + assertEventData(1, 2, 43, 0, [1, 2], { + 'hoverinfo': 'y', + 'hoverlabel.font.color': 'cyan' + }); assertHoverLabelStyle('rgb(255, 255, 255)', 'rgb(68, 68, 68)', 9, 'Arial', 'rgb(0, 255, 255)'); var label = d3.selectAll('g.hovertext'); diff --git a/test/jasmine/tests/scattermapbox_test.js b/test/jasmine/tests/scattermapbox_test.js index 6b65c668f95..5045fd8532d 100644 --- a/test/jasmine/tests/scattermapbox_test.js +++ b/test/jasmine/tests/scattermapbox_test.js @@ -591,8 +591,8 @@ describe('@noCI Test plotly events on a scattermapbox plot:', function() { expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); - expect(pt.lat).toEqual(undefined, 'points[0].lat'); - expect(pt.lon).toEqual(undefined, 'points[0].lon'); + expect(pt.lat).toEqual(10, 'points[0].lat'); + expect(pt.lon).toEqual(10, 'points[0].lon'); expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); @@ -635,8 +635,8 @@ describe('@noCI Test plotly events on a scattermapbox plot:', function() { expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); - expect(pt.lat).toEqual(undefined, 'points[0].lat'); - expect(pt.lon).toEqual(undefined, 'points[0].lon'); + expect(pt.lat).toEqual(10, 'points[0].lat'); + expect(pt.lon).toEqual(10, 'points[0].lon'); expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); @@ -672,8 +672,8 @@ describe('@noCI Test plotly events on a scattermapbox plot:', function() { expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); - expect(pt.lat).toEqual(undefined, 'points[0].lat'); - expect(pt.lon).toEqual(undefined, 'points[0].lon'); + expect(pt.lat).toEqual(10, 'points[0].lat'); + expect(pt.lon).toEqual(10, 'points[0].lon'); expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); @@ -704,8 +704,8 @@ describe('@noCI Test plotly events on a scattermapbox plot:', function() { expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); - expect(pt.lat).toEqual(undefined, 'points[0].lat'); - expect(pt.lon).toEqual(undefined, 'points[0].lon'); + expect(pt.lat).toEqual(10, 'points[0].lat'); + expect(pt.lon).toEqual(10, 'points[0].lon'); expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); expect(evt.clientX).toEqual(nearPos[0], 'event.clientX'); diff --git a/test/jasmine/tests/ternary_test.js b/test/jasmine/tests/ternary_test.js index 741d45d511b..ade4abf3d2c 100644 --- a/test/jasmine/tests/ternary_test.js +++ b/test/jasmine/tests/ternary_test.js @@ -163,7 +163,7 @@ describe('ternary plots', function() { expect(hoverData).not.toBe(undefined, 'firing on data points'); expect(Object.keys(hoverData)).toEqual([ 'data', 'fullData', 'curveNumber', 'pointNumber', - 'x', 'y', 'xaxis', 'yaxis' + 'x', 'y', 'xaxis', 'yaxis', 'a', 'b', 'c' ], 'returning the correct event data keys'); expect(hoverData.curveNumber).toEqual(0, 'returning the correct curve number'); expect(hoverData.pointNumber).toEqual(0, 'returning the correct point number'); @@ -172,7 +172,7 @@ describe('ternary plots', function() { expect(unhoverData).not.toBe(undefined, 'firing on data points'); expect(Object.keys(unhoverData)).toEqual([ 'data', 'fullData', 'curveNumber', 'pointNumber', - 'x', 'y', 'xaxis', 'yaxis' + 'x', 'y', 'xaxis', 'yaxis', 'a', 'b', 'c' ], 'returning the correct event data keys'); expect(unhoverData.curveNumber).toEqual(0, 'returning the correct curve number'); expect(unhoverData.pointNumber).toEqual(0, 'returning the correct point number'); @@ -195,7 +195,7 @@ describe('ternary plots', function() { expect(ptData).not.toBe(undefined, 'firing on data points'); expect(Object.keys(ptData)).toEqual([ 'data', 'fullData', 'curveNumber', 'pointNumber', - 'x', 'y', 'xaxis', 'yaxis' + 'x', 'y', 'xaxis', 'yaxis', 'a', 'b', 'c' ], 'returning the correct event data keys'); expect(ptData.curveNumber).toEqual(0, 'returning the correct curve number'); expect(ptData.pointNumber).toEqual(0, 'returning the correct point number'); @@ -404,7 +404,7 @@ describe('Test event property of interactions on a ternary plot:', function() { expect(Object.keys(pt)).toEqual([ 'data', 'fullData', 'curveNumber', 'pointNumber', 'x', 'y', - 'xaxis', 'yaxis' + 'xaxis', 'yaxis', 'a', 'b', 'c' ]); expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); @@ -415,6 +415,9 @@ describe('Test event property of interactions on a ternary plot:', function() { expect(pt.y).toEqual(undefined, 'points[0].y'); expect(typeof pt.xaxis).toEqual(typeof {}, 'points[0].xaxis'); expect(typeof pt.yaxis).toEqual(typeof {}, 'points[0].yaxis'); + expect(pt.a).toEqual(2, 'points[0].a'); + expect(pt.b).toEqual(1, 'points[0].b'); + expect(pt.c).toEqual(1, 'points[0].c'); expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); @@ -451,7 +454,7 @@ describe('Test event property of interactions on a ternary plot:', function() { expect(Object.keys(pt)).toEqual([ 'data', 'fullData', 'curveNumber', 'pointNumber', 'x', 'y', - 'xaxis', 'yaxis' + 'xaxis', 'yaxis', 'a', 'b', 'c' ]); expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); @@ -462,6 +465,9 @@ describe('Test event property of interactions on a ternary plot:', function() { expect(pt.y).toEqual(undefined, 'points[0].y'); expect(typeof pt.xaxis).toEqual(typeof {}, 'points[0].xaxis'); expect(typeof pt.yaxis).toEqual(typeof {}, 'points[0].yaxis'); + expect(pt.a).toEqual(2, 'points[0].a'); + expect(pt.b).toEqual(1, 'points[0].b'); + expect(pt.c).toEqual(1, 'points[0].c'); expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); @@ -495,7 +501,7 @@ describe('Test event property of interactions on a ternary plot:', function() { expect(Object.keys(pt)).toEqual([ 'data', 'fullData', 'curveNumber', 'pointNumber', 'x', 'y', - 'xaxis', 'yaxis' + 'xaxis', 'yaxis', 'a', 'b', 'c' ]); expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); @@ -506,6 +512,9 @@ describe('Test event property of interactions on a ternary plot:', function() { expect(pt.y).toEqual(undefined, 'points[0].y'); expect(typeof pt.xaxis).toEqual(typeof {}, 'points[0].xaxis'); expect(typeof pt.yaxis).toEqual(typeof {}, 'points[0].yaxis'); + expect(pt.a).toEqual(2, 'points[0].a'); + expect(pt.b).toEqual(1, 'points[0].b'); + expect(pt.c).toEqual(1, 'points[0].c'); expect(xaxes0).toEqual(pt.xaxis, 'xaxes[0]'); expect(xvals0).toEqual(-0.0016654247744483342, 'xaxes[0]'); @@ -538,7 +547,7 @@ describe('Test event property of interactions on a ternary plot:', function() { expect(Object.keys(pt)).toEqual([ 'data', 'fullData', 'curveNumber', 'pointNumber', 'x', 'y', - 'xaxis', 'yaxis' + 'xaxis', 'yaxis', 'a', 'b', 'c' ]); expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); @@ -549,6 +558,9 @@ describe('Test event property of interactions on a ternary plot:', function() { expect(pt.y).toEqual(undefined, 'points[0].y'); expect(typeof pt.xaxis).toEqual(typeof {}, 'points[0].xaxis'); expect(typeof pt.yaxis).toEqual(typeof {}, 'points[0].yaxis'); + expect(pt.a).toEqual(2, 'points[0].a'); + expect(pt.b).toEqual(1, 'points[0].b'); + expect(pt.c).toEqual(1, 'points[0].c'); expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); From 3399a5802f8a0aaec54520295ac2aa98bf1764b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 14 Jun 2017 15:32:51 -0400 Subject: [PATCH 8/8] fill selection event data in data, fullData and array values - to bring its point data on-par with hover/click/unhover --- src/plots/cartesian/select.js | 22 ++++++++++++- src/traces/scatter/select.js | 6 +--- test/jasmine/tests/select_test.js | 54 ++++++++++++++++++++++--------- 3 files changed, 60 insertions(+), 22 deletions(-) diff --git a/src/plots/cartesian/select.js b/src/plots/cartesian/select.js index 65835464e3d..cba5decac75 100644 --- a/src/plots/cartesian/select.js +++ b/src/plots/cartesian/select.js @@ -11,6 +11,7 @@ var polygon = require('../../lib/polygon'); var color = require('../../components/color'); +var appendArrayPointValue = require('../../components/fx/helpers').appendArrayPointValue; var axes = require('./axes'); var constants = require('./constants'); @@ -151,7 +152,9 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) { selection = []; for(i = 0; i < searchTraces.length; i++) { searchInfo = searchTraces[i]; - [].push.apply(selection, searchInfo.selectPoints(searchInfo, poly)); + [].push.apply(selection, fillSelectionItem( + searchInfo.selectPoints(searchInfo, poly), searchInfo + )); } eventData = {points: selection}; @@ -196,3 +199,20 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) { } }; }; + +function fillSelectionItem(selection, searchInfo) { + if(Array.isArray(selection)) { + var trace = searchInfo.cd[0].trace; + + for(var i = 0; i < selection.length; i++) { + var sel = selection[i]; + + sel.curveNumber = trace.index; + sel.data = trace._input; + sel.fullData = trace; + appendArrayPointValue(sel, trace, sel.pointNumber); + } + } + + return selection; +} diff --git a/src/traces/scatter/select.js b/src/traces/scatter/select.js index 4681e6f3d91..127179a790c 100644 --- a/src/traces/scatter/select.js +++ b/src/traces/scatter/select.js @@ -19,7 +19,6 @@ module.exports = function selectPoints(searchInfo, polygon) { ya = searchInfo.yaxis, selection = [], trace = cd[0].trace, - curveNumber = trace.index, marker = trace.marker, i, di, @@ -43,12 +42,9 @@ module.exports = function selectPoints(searchInfo, polygon) { if(polygon.contains([x, y])) { selection.push({ - curveNumber: curveNumber, pointNumber: i, x: di.x, - y: di.y, - // TODO generalize with hover/click data handler - id: di.id + y: di.y }); di.dim = 0; } diff --git a/test/jasmine/tests/select_test.js b/test/jasmine/tests/select_test.js index cbad8d1504c..37cf778b940 100644 --- a/test/jasmine/tests/select_test.js +++ b/test/jasmine/tests/select_test.js @@ -42,6 +42,23 @@ describe('select box and lasso', function() { expect(actual.y).toBeCloseToArray(expected.y, PRECISION); } + function assertEventData(actual, expected, msg) { + expect(actual.length).toBe(expected.length, msg + ' same number of pts'); + + expected.forEach(function(e, i) { + var a = actual[i]; + var m = msg + ' (pt ' + i + ')'; + + expect(a.data).toBeDefined(m + ' has data ref'); + expect(a.fullData).toBeDefined(m + ' has fullData ref'); + expect(Object.keys(a).length - 2).toBe(Object.keys(e).length, m + ' has correct number of keys'); + + Object.keys(e).forEach(function(k) { + expect(a[k]).toBe(e[k], m + ' ' + k); + }); + }); + } + describe('select elements', function() { var mockCopy = Lib.extendDeep({}, mock); mockCopy.layout.dragmode = 'select'; @@ -143,6 +160,10 @@ describe('select box and lasso', function() { describe('select events', function() { var mockCopy = Lib.extendDeep({}, mock); mockCopy.layout.dragmode = 'select'; + mockCopy.data[0].ids = mockCopy.data[0].x + .map(function(v) { return 'id-' + v; }); + mockCopy.data[0].customdata = mockCopy.data[0].y + .map(function(v) { return 'customdata-' + v; }); var gd; beforeEach(function(done) { @@ -175,38 +196,41 @@ describe('select box and lasso', function() { drag(selectPath); expect(selectingCnt).toEqual(1, 'with the correct selecting count'); - expect(selectingData.points).toEqual([{ + assertEventData(selectingData.points, [{ curveNumber: 0, pointNumber: 0, x: 0.002, y: 16.25, - id: undefined + id: 'id-0.002', + customdata: 'customdata-16.25' }, { curveNumber: 0, pointNumber: 1, x: 0.004, y: 12.5, - id: undefined - }], 'with the correct selecting points'); + id: 'id-0.004', + customdata: 'customdata-12.5' + }], 'with the correct selecting points (1)'); assertRange(selectingData.range, { x: [0.002000, 0.0046236], y: [0.10209191961595454, 24.512223978291406] }, 'with the correct selecting range'); - expect(selectedCnt).toEqual(1, 'with the correct selected count'); - expect(selectedData.points).toEqual([{ + assertEventData(selectedData.points, [{ curveNumber: 0, pointNumber: 0, x: 0.002, y: 16.25, - id: undefined + id: 'id-0.002', + customdata: 'customdata-16.25' }, { curveNumber: 0, pointNumber: 1, x: 0.004, y: 12.5, - id: undefined - }], 'with the correct selected points'); + id: 'id-0.004', + customdata: 'customdata-12.5' + }], 'with the correct selected points (2)'); assertRange(selectedData.range, { x: [0.002000, 0.0046236], y: [0.10209191961595454, 24.512223978291406] @@ -255,22 +279,20 @@ describe('select box and lasso', function() { drag(lassoPath); expect(selectingCnt).toEqual(3, 'with the correct selecting count'); - expect(selectingData.points).toEqual([{ + assertEventData(selectingData.points, [{ curveNumber: 0, pointNumber: 10, x: 0.099, - y: 2.75, - id: undefined - }], 'with the correct selecting points'); + y: 2.75 + }], 'with the correct selecting points (1)'); expect(selectedCnt).toEqual(1, 'with the correct selected count'); - expect(selectedData.points).toEqual([{ + assertEventData(selectedData.points, [{ curveNumber: 0, pointNumber: 10, x: 0.099, y: 2.75, - id: undefined - }], 'with the correct selected points'); + }], 'with the correct selected points (2)'); doubleClick(250, 200).then(function() { expect(doubleClickData).toBe(null, 'with the correct deselect data');