diff --git a/lib/candlestick.js b/lib/candlestick.js new file mode 100644 index 00000000000..b4b0f06e6de --- /dev/null +++ b/lib/candlestick.js @@ -0,0 +1,11 @@ +/** +* 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'; + +module.exports = require('../src/traces/candlestick'); diff --git a/lib/index-finance.js b/lib/index-finance.js new file mode 100644 index 00000000000..c612bd02df8 --- /dev/null +++ b/lib/index-finance.js @@ -0,0 +1,21 @@ +/** +* 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 Plotly = require('./core'); + +Plotly.register([ + require('./bar'), + require('./histogram'), + require('./pie'), + require('./ohlc'), + require('./candlestick') +]); + +module.exports = Plotly; diff --git a/lib/index.js b/lib/index.js index 359de92a48e..c578165aadd 100644 --- a/lib/index.js +++ b/lib/index.js @@ -20,15 +20,22 @@ Plotly.register([ require('./histogram2dcontour'), require('./pie'), require('./contour'), + require('./scatterternary'), + require('./scatter3d'), require('./surface'), require('./mesh3d'), + require('./scattergeo'), require('./choropleth'), + require('./scattergl'), require('./pointcloud'), - require('./scatterternary'), - require('./scattermapbox') + + require('./scattermapbox'), + + require('./ohlc'), + require('./candlestick') ]); // transforms diff --git a/lib/ohlc.js b/lib/ohlc.js new file mode 100644 index 00000000000..989e77e521c --- /dev/null +++ b/lib/ohlc.js @@ -0,0 +1,11 @@ +/** +* 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'; + +module.exports = require('../src/traces/ohlc'); diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index fd08a20baa5..17258ffbd21 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -367,10 +367,28 @@ function drawTexts(g, gd) { .call(textLayout) .on('edit', function(text) { this.attr({'data-unformatted': text}); + this.text(text) .call(textLayout); + if(!this.text()) text = ' \u0020\u0020 '; - Plotly.restyle(gd, 'name', text, traceIndex); + + var fullInput = legendItem.trace._fullInput || {}, + astr; + + // N.B. this block isn't super clean, + // is unfortunately untested at the moment, + // and only works for for 'ohlc' and 'candlestick', + // but should be generalized for other one-to-many transforms + if(['ohlc', 'candlestick'].indexOf(fullInput.type) !== -1) { + var transforms = legendItem.trace.transforms, + direction = transforms[transforms.length - 1].direction; + + astr = direction + '.legenditem.name'; + } + else astr = 'name'; + + Plotly.restyle(gd, astr, text, traceIndex); }); } else text.call(textLayout); diff --git a/src/lib/coerce.js b/src/lib/coerce.js index d0f8565847f..11f9153cac1 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -13,6 +13,7 @@ var isNumeric = require('fast-isnumeric'); var tinycolor = require('tinycolor2'); var nestedProperty = require('./nested_property'); var isPlainObject = require('./is_plain_object'); +var filterUnique = require('./filter_unique'); var getColorscale = require('../components/colorscale/get_scale'); var colorscaleNames = Object.keys(require('../components/colorscale/scales')); @@ -457,5 +458,17 @@ exports.findArrayAttributes = function(trace) { exports.crawl(trace._module.attributes, callback); + // Look into the fullInput module attributes for array attributes + // to make sure that 'custom' array attributes are detected. + // + // 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) { + exports.crawl(trace._fullInput._module.attributes, callback); + + arrayAttributes = filterUnique(arrayAttributes); + } + return arrayAttributes; }; diff --git a/src/lib/filter_unique.js b/src/lib/filter_unique.js new file mode 100644 index 00000000000..d31028d7bac --- /dev/null +++ b/src/lib/filter_unique.js @@ -0,0 +1,49 @@ +/** +* 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'; + + +/** + * Return news array containing only the unique items + * found in input array. + * + * IMPORTANT: Note that items are considered unique + * if `String({})` is unique. For example; + * + * Lib.filterUnique([ { a: 1 }, { b: 2 } ]) + * + * returns [{ a: 1 }] + * + * and + * + * Lib.filterUnique([ '1', 1 ]) + * + * returns ['1'] + * + * + * @param {array} array base array + * @return {array} new filtered array + */ +module.exports = function filterUnique(array) { + var seen = {}, + out = [], + j = 0; + + for(var i = 0; i < array.length; i++) { + var item = array[i]; + + if(seen[item] !== 1) { + seen[item] = 1; + out[j++] = item; + } + } + + return out; +}; diff --git a/src/lib/index.js b/src/lib/index.js index 3518fca5d31..37d492dbf23 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -75,6 +75,8 @@ lib.error = loggersModule.error; lib.notifier = require('./notifier'); +lib.filterUnique = require('./filter_unique'); + /** * swap x and y of the same attribute in container cont * specify attr with a ? in place of x/y diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index 9b1976b4f7f..640912f281b 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -140,7 +140,7 @@ Plotly.plot = function(gd, data, layout, config) { // generate calcdata, if we need to // to force redoing calcdata, just delete it before calling Plotly.plot - var recalc = !gd.calcdata || gd.calcdata.length !== (gd.data || []).length; + var recalc = !gd.calcdata || gd.calcdata.length !== (gd._fullData || []).length; if(recalc) Plots.doCalcdata(gd); // in case it has changed, attach fullData traces to calcdata @@ -1245,6 +1245,7 @@ function _restyle(gd, aobj, _traces) { 'histfunc', 'histnorm', 'text', 'x', 'y', 'z', 'a', 'b', 'c', + 'open', 'high', 'low', 'close', 'xtype', 'x0', 'dx', 'ytype', 'y0', 'dy', 'xaxis', 'yaxis', 'line.width', 'connectgaps', 'transpose', 'zsmooth', @@ -1287,7 +1288,8 @@ function _restyle(gd, aobj, _traces) { // TODO: could we break this out as well? var autorangeAttrs = [ 'marker', 'marker.size', 'textfont', - 'boxpoints', 'jitter', 'pointpos', 'whiskerwidth', 'boxmean' + 'boxpoints', 'jitter', 'pointpos', 'whiskerwidth', 'boxmean', + 'tickwidth' ]; // replotAttrs attributes need a replot (because different diff --git a/src/plots/plots.js b/src/plots/plots.js index 56aa8a9b696..c504faa0c37 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -369,7 +369,7 @@ plots.supplyDefaults = function(gd) { // then do the data newFullLayout._globalTransforms = (gd._context || {}).globalTransforms; - plots.supplyDataDefaults(newData, newFullData, newFullLayout); + plots.supplyDataDefaults(newData, newFullData, newLayout, newFullLayout); // attach helper method to check whether a plot type is present on graph newFullLayout._has = plots._hasPlotType.bind(newFullLayout); @@ -530,7 +530,7 @@ function relinkPrivateKeys(toContainer, fromContainer) { var isPlainObject = Lib.isPlainObject, isArray = Array.isArray; - var keys = Object.keys(fromContainer); + var keys = Object.keys(fromContainer || {}); for(var i = 0; i < keys.length; i++) { var k = keys[i], @@ -593,9 +593,9 @@ plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLa } }; -plots.supplyDataDefaults = function(dataIn, dataOut, layout) { - var modules = layout._modules = [], - basePlotModules = layout._basePlotModules = [], +plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) { + var modules = fullLayout._modules = [], + basePlotModules = fullLayout._basePlotModules = [], cnt = 0; function pushModule(fullTrace) { @@ -612,18 +612,18 @@ plots.supplyDataDefaults = function(dataIn, dataOut, layout) { for(var i = 0; i < dataIn.length; i++) { var trace = dataIn[i], - fullTrace = plots.supplyTraceDefaults(trace, cnt, layout); + fullTrace = plots.supplyTraceDefaults(trace, cnt, fullLayout); fullTrace.index = i; fullTrace._input = trace; fullTrace._expandedIndex = cnt; if(fullTrace.transforms && fullTrace.transforms.length) { - var expandedTraces = applyTransforms(fullTrace, dataOut, layout); + var expandedTraces = applyTransforms(fullTrace, dataOut, layout, fullLayout); for(var j = 0; j < expandedTraces.length; j++) { var expandedTrace = expandedTraces[j], - fullExpandedTrace = plots.supplyTraceDefaults(expandedTrace, cnt, layout); + fullExpandedTrace = plots.supplyTraceDefaults(expandedTrace, cnt, fullLayout); // mutate uid here using parent uid and expanded index // to promote consistency between update calls @@ -805,7 +805,7 @@ function supplyTransformDefaults(traceIn, traceOut, layout) { if(!_module) Lib.warn('Unrecognized transform type ' + type + '.'); if(_module && _module.supplyDefaults) { - transformOut = _module.supplyDefaults(transformIn, traceOut, layout); + transformOut = _module.supplyDefaults(transformIn, traceOut, layout, traceIn); transformOut.type = type; } else { @@ -816,7 +816,7 @@ function supplyTransformDefaults(traceIn, traceOut, layout) { } } -function applyTransforms(fullTrace, fullData, layout) { +function applyTransforms(fullTrace, fullData, layout, fullLayout) { var container = fullTrace.transforms, dataOut = [fullTrace]; @@ -830,6 +830,7 @@ function applyTransforms(fullTrace, fullData, layout) { fullTrace: fullTrace, fullData: fullData, layout: layout, + fullLayout: fullLayout, transformIndex: i }); } diff --git a/src/traces/candlestick/attributes.js b/src/traces/candlestick/attributes.js new file mode 100644 index 00000000000..64b7be83203 --- /dev/null +++ b/src/traces/candlestick/attributes.js @@ -0,0 +1,56 @@ +/** +* 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'); +var OHLCattrs = require('../ohlc/attributes'); +var boxAttrs = require('../box/attributes'); + +var directionAttrs = { + name: OHLCattrs.increasing.name, + showlegend: OHLCattrs.increasing.showlegend, + + line: { + color: Lib.extendFlat({}, boxAttrs.line.color), + width: Lib.extendFlat({}, boxAttrs.line.width) + }, + + fillcolor: Lib.extendFlat({}, boxAttrs.fillcolor), +}; + +module.exports = { + x: OHLCattrs.x, + open: OHLCattrs.open, + high: OHLCattrs.high, + low: OHLCattrs.low, + close: OHLCattrs.close, + + line: { + width: Lib.extendFlat({}, boxAttrs.line.width, { + description: [ + boxAttrs.line.width.description, + 'Note that this style setting can also be set per', + 'direction via `increasing.line.width` and', + '`decreasing.line.width`.' + ].join(' ') + }) + }, + + increasing: Lib.extendDeep({}, directionAttrs, { + line: { color: { dflt: OHLCattrs.increasing.line.color.dflt } } + }), + + decreasing: Lib.extendDeep({}, directionAttrs, { + line: { color: { dflt: OHLCattrs.decreasing.line.color.dflt } } + }), + + text: OHLCattrs.text, + whiskerwidth: Lib.extendFlat({}, boxAttrs.whiskerwidth, { dflt: 0 }) +}; diff --git a/src/traces/candlestick/defaults.js b/src/traces/candlestick/defaults.js new file mode 100644 index 00000000000..c6b4538ea53 --- /dev/null +++ b/src/traces/candlestick/defaults.js @@ -0,0 +1,46 @@ +/** +* 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'); +var handleOHLC = require('../ohlc/ohlc_defaults'); +var handleDirectionDefaults = require('../ohlc/direction_defaults'); +var helpers = require('../ohlc/helpers'); +var attributes = require('./attributes'); + +module.exports = function supplyDefaults(traceIn, traceOut) { + helpers.pushDummyTransformOpts(traceIn, traceOut); + + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + + var len = handleOHLC(traceIn, traceOut, coerce); + if(len === 0) { + traceOut.visible = false; + return; + } + + coerce('line.width'); + + handleDirection(traceIn, traceOut, coerce, 'increasing'); + handleDirection(traceIn, traceOut, coerce, 'decreasing'); + + coerce('text'); + coerce('whiskerwidth'); +}; + +function handleDirection(traceIn, traceOut, coerce, direction) { + handleDirectionDefaults(traceIn, traceOut, coerce, direction); + + coerce(direction + '.line.color'); + coerce(direction + '.line.width', traceOut.line.width); + coerce(direction + '.fillcolor'); +} diff --git a/src/traces/candlestick/index.js b/src/traces/candlestick/index.js new file mode 100644 index 00000000000..ccd1d5427aa --- /dev/null +++ b/src/traces/candlestick/index.js @@ -0,0 +1,40 @@ +/** +* 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 register = require('../../plot_api/register'); + +module.exports = { + moduleType: 'trace', + name: 'candlestick', + basePlotModule: require('../../plots/cartesian'), + categories: ['cartesian', 'showLegend'], + meta: { + description: [ + 'The candlestick is a style of financial chart describing', + 'open, high, low and close for a given `x` coordinate (most likely time).', + + 'The boxes represent the spread between the `open` and `close` values and', + 'the lines represent the spread between the `low` and `high` values', + + 'Sample points where the close value is higher (lower) then the open', + 'value are called increasing (decreasing).', + + 'By default, increasing candles are drawn in green whereas', + 'decreasing are drawn in red.' + ].join(' ') + }, + + attributes: require('./attributes'), + supplyDefaults: require('./defaults'), +}; + +register(require('../box')); +register(require('./transform')); diff --git a/src/traces/candlestick/transform.js b/src/traces/candlestick/transform.js new file mode 100644 index 00000000000..6d32870e3ce --- /dev/null +++ b/src/traces/candlestick/transform.js @@ -0,0 +1,125 @@ +/** +* 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'); +var helpers = require('../ohlc/helpers'); + +exports.moduleType = 'transform'; + +exports.name = 'candlestick'; + +exports.attributes = {}; + +exports.supplyDefaults = function(transformIn, traceOut, layout, traceIn) { + helpers.clearEphemeralTransformOpts(traceIn); + helpers.copyOHLC(transformIn, traceOut); + + return transformIn; +}; + +exports.transform = function transform(dataIn, state) { + var dataOut = []; + + for(var i = 0; i < dataIn.length; i++) { + var traceIn = dataIn[i]; + + if(traceIn.type !== 'candlestick') { + dataOut.push(traceIn); + continue; + } + + dataOut.push( + makeTrace(traceIn, state, 'increasing'), + makeTrace(traceIn, state, 'decreasing') + ); + } + + helpers.addRangeSlider(state.layout); + + return dataOut; +}; + +function makeTrace(traceIn, state, direction) { + var traceOut = { + type: 'box', + boxpoints: false, + + visible: traceIn.visible, + hoverinfo: traceIn.hoverinfo, + opacity: traceIn.opacity, + xaxis: traceIn.xaxis, + yaxis: traceIn.yaxis, + + transforms: helpers.makeTransform(traceIn, state, direction) + }; + + // the rest of below may not have been coerced + + var directionOpts = traceIn[direction]; + + if(directionOpts) { + Lib.extendFlat(traceOut, { + + // to make autotype catch date axes soon!! + x: traceIn.x || [0], + + // concat low and high to get correct autorange + y: [].concat(traceIn.low).concat(traceIn.high), + + whiskerwidth: traceIn.whiskerwidth, + text: traceIn.text, + + name: directionOpts.name, + showlegend: directionOpts.showlegend, + line: directionOpts.line, + fillcolor: directionOpts.fillcolor + }); + } + + return traceOut; +} + +exports.calcTransform = function calcTransform(gd, trace, opts) { + var direction = opts.direction, + filterFn = helpers.getFilterFn(direction); + + var open = trace.open, + high = trace.high, + low = trace.low, + close = trace.close; + + var len = open.length, + x = [], + y = []; + + var appendX = trace._fullInput.x ? + function(i) { + var v = trace.x[i]; + x.push(v, v, v, v, v, v); + } : + function(i) { + x.push(i, i, i, i, i, i); + }; + + var appendY = function(o, h, l, c) { + y.push(l, o, c, c, c, h); + }; + + for(var i = 0; i < len; i++) { + if(filterFn(open[i], close[i])) { + appendX(i); + appendY(open[i], high[i], low[i], close[i]); + } + } + + trace.x = x; + trace.y = y; +}; diff --git a/src/traces/ohlc/attributes.js b/src/traces/ohlc/attributes.js new file mode 100644 index 00000000000..b4168829c97 --- /dev/null +++ b/src/traces/ohlc/attributes.js @@ -0,0 +1,133 @@ +/** +* 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'); +var scatterAttrs = require('../scatter/attributes'); + +var INCREASING_COLOR = '#3D9970'; +var DECREASING_COLOR = '#FF4136'; + +var lineAttrs = scatterAttrs.line; + +var directionAttrs = { + name: { + valType: 'string', + role: 'info', + description: [ + 'Sets the segment name.', + 'The segment name appear as the legend item and on hover.' + ].join(' ') + }, + + showlegend: { + valType: 'boolean', + role: 'info', + dflt: true, + description: [ + 'Determines whether or not an item corresponding to this', + 'segment is shown in the legend.' + ].join(' ') + }, + + line: { + color: Lib.extendFlat({}, lineAttrs.color), + width: Lib.extendFlat({}, lineAttrs.width), + dash: Lib.extendFlat({}, lineAttrs.dash), + } +}; + +module.exports = { + + x: { + valType: 'data_array', + description: [ + 'Sets the x coordinates.', + 'If absent, linear coordinate will be generated.' + ].join(' ') + }, + + open: { + valType: 'data_array', + dflt: [], + description: 'Sets the open values.' + }, + + high: { + valType: 'data_array', + dflt: [], + description: 'Sets the high values.' + }, + + low: { + valType: 'data_array', + dflt: [], + description: 'Sets the low values.' + }, + + close: { + valType: 'data_array', + dflt: [], + description: 'Sets the close values.' + }, + + line: { + width: Lib.extendFlat({}, lineAttrs.width, { + description: [ + lineAttrs.width, + 'Note that this style setting can also be set per', + 'direction via `increasing.line.width` and', + '`decreasing.line.width`.' + ].join(' ') + }), + dash: Lib.extendFlat({}, lineAttrs.dash, { + description: [ + lineAttrs.dash, + 'Note that this style setting can also be set per', + 'direction via `increasing.line.dash` and', + '`decreasing.line.dash`.' + ].join(' ') + }), + }, + + increasing: Lib.extendDeep({}, directionAttrs, { + line: { color: { dflt: INCREASING_COLOR } } + }), + + decreasing: Lib.extendDeep({}, directionAttrs, { + line: { color: { dflt: DECREASING_COLOR } } + }), + + text: { + valType: 'string', + role: 'info', + dflt: '', + arrayOk: true, + description: [ + 'Sets hover text elements associated with each sample point.', + 'If a single string, the same string appears over', + 'all the data points.', + 'If an array of string, the items are mapped in order to', + 'this trace\'s sample points.' + ].join(' ') + }, + + tickwidth: { + valType: 'number', + min: 0, + max: 0.5, + dflt: 0.3, + role: 'style', + description: [ + 'Sets the width of the open/close tick marks', + 'relative to the *x* minimal interval.' + ].join(' ') + } +}; diff --git a/src/traces/ohlc/defaults.js b/src/traces/ohlc/defaults.js new file mode 100644 index 00000000000..791535de251 --- /dev/null +++ b/src/traces/ohlc/defaults.js @@ -0,0 +1,47 @@ +/** +* 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'); +var handleOHLC = require('./ohlc_defaults'); +var handleDirectionDefaults = require('./direction_defaults'); +var attributes = require('./attributes'); +var helpers = require('./helpers'); + +module.exports = function supplyDefaults(traceIn, traceOut) { + helpers.pushDummyTransformOpts(traceIn, traceOut); + + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + + var len = handleOHLC(traceIn, traceOut, coerce); + if(len === 0) { + traceOut.visible = false; + return; + } + + coerce('line.width'); + coerce('line.dash'); + + handleDirection(traceIn, traceOut, coerce, 'increasing'); + handleDirection(traceIn, traceOut, coerce, 'decreasing'); + + coerce('text'); + coerce('tickwidth'); +}; + +function handleDirection(traceIn, traceOut, coerce, direction) { + handleDirectionDefaults(traceIn, traceOut, coerce, direction); + + coerce(direction + '.line.color'); + coerce(direction + '.line.width', traceOut.line.width); + coerce(direction + '.line.dash', traceOut.line.dash); +} diff --git a/src/traces/ohlc/direction_defaults.js b/src/traces/ohlc/direction_defaults.js new file mode 100644 index 00000000000..3abda8a2e68 --- /dev/null +++ b/src/traces/ohlc/direction_defaults.js @@ -0,0 +1,24 @@ +/** +* 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'; + + +module.exports = function handleDirectionDefaults(traceIn, traceOut, coerce, direction) { + coerce(direction + '.showlegend'); + + // trace-wide *showlegend* overrides direction *showlegend* + if(traceIn.showlegend === false) { + traceOut[direction].showlegend = false; + } + + var nameDflt = traceOut.name + ' - ' + direction; + + coerce(direction + '.name', nameDflt); +}; diff --git a/src/traces/ohlc/helpers.js b/src/traces/ohlc/helpers.js new file mode 100644 index 00000000000..8167f732dbd --- /dev/null +++ b/src/traces/ohlc/helpers.js @@ -0,0 +1,110 @@ +/** +* 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'); + +// This routine gets called during the trace supply-defaults step. +// +// This is a hacky way to make 'ohlc' and 'candlestick' trace types +// go through the transform machinery. +// +// Note that, we must mutate user data (here traceIn) as opposed +// to full data (here traceOut) as - at the moment - transform +// defaults (which are called after trace defaults) start +// from a clear transforms container. The mutations inflicted are +// cleared in exports.clearEphemeralTransformOpts. +exports.pushDummyTransformOpts = function(traceIn, traceOut) { + var transformOpts = { + + // give dummy transform the same type as trace + type: traceOut.type, + + // track ephemeral transforms in user data + _ephemeral: true + }; + + if(Array.isArray(traceIn.transforms)) { + traceIn.transforms.push(transformOpts); + } + else { + traceIn.transforms = [transformOpts]; + } +}; + +// This routine gets called during the transform supply-defaults step +// where it clears ephemeral transform opts in user data +// and effectively put back user date in its pre-supplyDefaults state. +exports.clearEphemeralTransformOpts = function(traceIn) { + var transformsIn = traceIn.transforms; + + if(!Array.isArray(transformsIn)) return; + + for(var i = 0; i < transformsIn.length; i++) { + if(transformsIn[i]._ephemeral) transformsIn.splice(i, 1); + } + + if(transformsIn.length === 0) delete traceIn.transforms; +}; + +// This routine gets called during the transform supply-defaults step +// where it passes 'ohlc' and 'candlestick' attributes +// (found the transform container via exports.makeTransform) +// to the traceOut container such that they can +// be compatible with filter and groupby transforms. +// +// Note that this routine only has an effect during the +// second round of transform defaults done on generated traces +exports.copyOHLC = function(container, traceOut) { + if(container.open) traceOut.open = container.open; + if(container.high) traceOut.high = container.high; + if(container.low) traceOut.low = container.low; + if(container.close) traceOut.close = container.close; +}; + +// This routine gets called during the applyTransform step. +// +// We need to track trace attributes and which direction +// ('increasing' or 'decreasing') +// the generated correspond to for the calcTransform step. +// +// To make sure that the attributes reach the calcTransform, +// store it in the transform opts object. +exports.makeTransform = function(traceIn, state, direction) { + var out = Lib.extendFlat([], traceIn.transforms); + + out[state.transformIndex] = { + type: traceIn.type, + direction: direction, + + // these are copied to traceOut during exports.copyOHLC + open: traceIn.open, + high: traceIn.high, + low: traceIn.low, + close: traceIn.close + }; + + return out; +}; + +exports.getFilterFn = function(direction) { + switch(direction) { + case 'increasing': + return function(o, c) { return o <= c; }; + + case 'decreasing': + return function(o, c) { return o > c; }; + } +}; + +exports.addRangeSlider = function(layout) { + if(!layout.xaxis) layout.xaxis = {}; + if(!layout.xaxis.rangeslider) layout.xaxis.rangeslider = {}; +}; diff --git a/src/traces/ohlc/index.js b/src/traces/ohlc/index.js new file mode 100644 index 00000000000..edaa23a1d32 --- /dev/null +++ b/src/traces/ohlc/index.js @@ -0,0 +1,40 @@ +/** +* 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 register = require('../../plot_api/register'); + +module.exports = { + moduleType: 'trace', + name: 'ohlc', + basePlotModule: require('../../plots/cartesian'), + categories: ['cartesian', 'showLegend'], + meta: { + description: [ + 'The ohlc (short for Open-High-Low-Close) is a style of financial chart describing', + 'open, high, low and close for a given `x` coordinate (most likely time).', + + 'The tip of the lines represent the `low` and `high` values and', + 'the horizontal segments represent the `open` and `close` values.', + + 'Sample points where the close value is higher (lower) then the open', + 'value are called increasing (decreasing).', + + 'By default, increasing candles are drawn in green whereas', + 'decreasing are drawn in red.' + ].join(' ') + }, + + attributes: require('./attributes'), + supplyDefaults: require('./defaults'), +}; + +register(require('../scatter')); +register(require('./transform')); diff --git a/src/traces/ohlc/ohlc_defaults.js b/src/traces/ohlc/ohlc_defaults.js new file mode 100644 index 00000000000..33e335f4188 --- /dev/null +++ b/src/traces/ohlc/ohlc_defaults.js @@ -0,0 +1,34 @@ +/** +* 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'; + +module.exports = function handleOHLC(traceIn, traceOut, coerce) { + var len; + + var x = coerce('x'), + open = coerce('open'), + high = coerce('high'), + low = coerce('low'), + close = coerce('close'); + + len = Math.min(open.length, high.length, low.length, close.length); + + if(x) { + len = Math.min(len, x.length); + if(len < x.length) traceOut.x = x.slice(0, len); + } + + if(len < open.length) traceOut.open = open.slice(0, len); + if(len < high.length) traceOut.high = high.slice(0, len); + if(len < low.length) traceOut.low = low.slice(0, len); + if(len < close.length) traceOut.close = close.slice(0, len); + + return len; +}; diff --git a/src/traces/ohlc/transform.js b/src/traces/ohlc/transform.js new file mode 100644 index 00000000000..f7e0b959818 --- /dev/null +++ b/src/traces/ohlc/transform.js @@ -0,0 +1,205 @@ +/** +* 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'); +var helpers = require('./helpers'); +var Axes = require('../../plots/cartesian/axes'); +var axisIds = require('../../plots/cartesian/axis_ids'); + +exports.moduleType = 'transform'; + +exports.name = 'ohlc'; + +exports.attributes = {}; + +exports.supplyDefaults = function(transformIn, traceOut, layout, traceIn) { + helpers.clearEphemeralTransformOpts(traceIn); + helpers.copyOHLC(transformIn, traceOut); + + return transformIn; +}; + +exports.transform = function transform(dataIn, state) { + var dataOut = []; + + for(var i = 0; i < dataIn.length; i++) { + var traceIn = dataIn[i]; + + if(traceIn.type !== 'ohlc') { + dataOut.push(traceIn); + continue; + } + + dataOut.push( + makeTrace(traceIn, state, 'increasing'), + makeTrace(traceIn, state, 'decreasing') + ); + } + + helpers.addRangeSlider(state.layout); + + return dataOut; +}; + +function makeTrace(traceIn, state, direction) { + var traceOut = { + type: 'scatter', + mode: 'lines', + connectgaps: false, + + visible: traceIn.visible, + opacity: traceIn.opacity, + xaxis: traceIn.xaxis, + yaxis: traceIn.yaxis, + + hoverinfo: makeHoverInfo(traceIn), + transforms: helpers.makeTransform(traceIn, state, direction) + }; + + // the rest of below may not have been coerced + + var directionOpts = traceIn[direction]; + + if(directionOpts) { + Lib.extendFlat(traceOut, { + + // to make autotype catch date axes soon!! + x: traceIn.x || [0], + + // concat low and high to get correct autorange + y: [].concat(traceIn.low).concat(traceIn.high), + + text: traceIn.text, + + name: directionOpts.name, + showlegend: directionOpts.showlegend, + line: directionOpts.line + }); + } + + return traceOut; +} + +// Let scatter hoverPoint format 'x' coordinates, if desired. +// +// Note that, this solution isn't perfect: it shows open and close +// values at slightly different 'x' coordinates then the rest of the +// segments, but is for more robust than calling `Axes.tickText` during +// calcTransform. +// +// A future iteration should perhaps try to add a hook for transforms in +// the hoverPoints handlers. +function makeHoverInfo(traceIn) { + var hoverinfo = traceIn.hoverinfo; + + if(hoverinfo === 'all') return 'x+text'; + + var parts = hoverinfo.split('+'), + hasX = parts.indexOf('x') !== -1, + hasText = parts.indexOf('text') !== -1; + + if(hasX) { + if(hasText) return 'x+text'; + else return 'x'; + + } + else return 'text'; +} + +exports.calcTransform = function calcTransform(gd, trace, opts) { + var direction = opts.direction, + filterFn = helpers.getFilterFn(direction); + + var xa = axisIds.getFromTrace(gd, trace, 'x'), + ya = axisIds.getFromTrace(gd, trace, 'y'), + tickWidth = convertTickWidth(trace.x, xa, trace._fullInput.tickwidth); + + var open = trace.open, + high = trace.high, + low = trace.low, + close = trace.close, + textIn = trace.text; + + var len = open.length, + x = [], + y = [], + textOut = []; + + var getXItem = trace._fullInput.x ? + function(i) { return xa.d2c(trace.x[i]); } : + function(i) { return i; }; + + var getTextItem = Array.isArray(textIn) ? + function(i) { return textIn[i] || ''; } : + function() { return textIn; }; + + var appendX = function(i) { + var v = getXItem(i); + x.push(v - tickWidth, v, v, v, v, v + tickWidth, null); + }; + + var appendY = function(o, h, l, c) { + y.push(o, o, h, l, c, c, null); + }; + + var format = function(ax, val) { + return Axes.tickText(ax, ax.c2l(val), 'hover').text; + }; + + var hoverinfo = trace._fullInput.hoverinfo, + hoverParts = hoverinfo.split('+'), + hasAll = hoverinfo === 'all', + hasY = hasAll || hoverParts.indexOf('y') !== -1, + hasText = hasAll || hoverParts.indexOf('text') !== -1; + + var appendText = function(i, o, h, l, c) { + var t = []; + + if(hasY) { + t.push('Open: ' + format(ya, o)); + t.push('High: ' + format(ya, h)); + t.push('Low: ' + format(ya, l)); + t.push('Close: ' + format(ya, c)); + } + + if(hasText) t.push(getTextItem(i)); + + var _t = t.join('
'); + + textOut.push(_t, _t, _t, _t, _t, _t, null); + }; + + for(var i = 0; i < len; i++) { + if(filterFn(open[i], close[i])) { + appendX(i); + appendY(open[i], high[i], low[i], close[i]); + appendText(i, open[i], high[i], low[i], close[i]); + } + } + + trace.x = x; + trace.y = y; + trace.text = textOut; +}; + +function convertTickWidth(coords, ax, tickWidth) { + if(coords.length < 2) return tickWidth; + + var _coords = coords.map(ax.d2c), + minDTick = Math.abs(_coords[1] - _coords[0]); + + for(var i = 1; i < _coords.length - 1; i++) { + var dist = Math.abs(_coords[i + 1] - _coords[i]); + minDTick = Math.min(dist, minDTick); + } + + return minDTick * tickWidth; +} diff --git a/src/transforms/groupby.js b/src/transforms/groupby.js index f736c0e1a75..0cce190eab3 100644 --- a/src/transforms/groupby.js +++ b/src/transforms/groupby.js @@ -117,16 +117,13 @@ function transformOne(trace, state) { var opts = state.transform; var groups = trace.transforms[state.transformIndex].groups; - var groupNames = groups.filter(function(g, i, self) { - return self.indexOf(g) === i; - }); - if(!(Array.isArray(groups)) || groups.length === 0) { return trace; } - var newData = new Array(groupNames.length); - var len = groups.length; + var groupNames = Lib.filterUnique(groups), + newData = new Array(groupNames.length), + len = groups.length; var arrayAttrs = Lib.findArrayAttributes(trace); diff --git a/tasks/util/constants.js b/tasks/util/constants.js index f5986e5a92b..c867dd2cf26 100644 --- a/tasks/util/constants.js +++ b/tasks/util/constants.js @@ -13,7 +13,7 @@ var pathToTopojsonSrc = path.join( ); var partialBundleNames = [ - 'basic', 'cartesian', 'geo', 'gl3d', 'gl2d', 'mapbox' + 'basic', 'cartesian', 'geo', 'gl3d', 'gl2d', 'mapbox', 'finance' ]; var partialBundlePaths = partialBundleNames.map(function(name) { diff --git a/test/image/baselines/candlestick_double-y-axis.png b/test/image/baselines/candlestick_double-y-axis.png new file mode 100644 index 00000000000..8c6c223bc13 Binary files /dev/null and b/test/image/baselines/candlestick_double-y-axis.png differ diff --git a/test/image/baselines/finance_style.png b/test/image/baselines/finance_style.png new file mode 100644 index 00000000000..6bf7d6fa708 Binary files /dev/null and b/test/image/baselines/finance_style.png differ diff --git a/test/image/baselines/ohlc_first.png b/test/image/baselines/ohlc_first.png new file mode 100644 index 00000000000..4ce83c5aa83 Binary files /dev/null and b/test/image/baselines/ohlc_first.png differ diff --git a/test/image/mocks/candlestick_double-y-axis.json b/test/image/mocks/candlestick_double-y-axis.json new file mode 100644 index 00000000000..3d94eaa9e0f --- /dev/null +++ b/test/image/mocks/candlestick_double-y-axis.json @@ -0,0 +1,1973 @@ +{ + "data": [ + { + "type": "candlestick", + "name": "ABC", + "x": [ + "2016-09-30", + "2016-09-29", + "2016-09-28", + "2016-09-27", + "2016-09-26", + "2016-09-23", + "2016-09-22", + "2016-09-21", + "2016-09-20", + "2016-09-19", + "2016-09-16", + "2016-09-15", + "2016-09-14", + "2016-09-13", + "2016-09-12", + "2016-09-09", + "2016-09-08", + "2016-09-07", + "2016-09-06", + "2016-09-02", + "2016-09-01", + "2016-08-31", + "2016-08-30", + "2016-08-29", + "2016-08-26", + "2016-08-25", + "2016-08-24", + "2016-08-23", + "2016-08-22", + "2016-08-19", + "2016-08-18", + "2016-08-17", + "2016-08-16", + "2016-08-15", + "2016-08-12", + "2016-08-11", + "2016-08-10", + "2016-08-09", + "2016-08-08", + "2016-08-05", + "2016-08-04", + "2016-08-03", + "2016-08-02", + "2016-08-01", + "2016-07-29", + "2016-07-28", + "2016-07-27", + "2016-07-26", + "2016-07-25", + "2016-07-22", + "2016-07-21", + "2016-07-20", + "2016-07-19", + "2016-07-18", + "2016-07-15", + "2016-07-14", + "2016-07-13", + "2016-07-12", + "2016-07-11", + "2016-07-08", + "2016-07-07", + "2016-07-06", + "2016-07-05", + "2016-07-01", + "2016-06-30", + "2016-06-29", + "2016-06-28", + "2016-06-27", + "2016-06-24", + "2016-06-23", + "2016-06-22", + "2016-06-21", + "2016-06-20", + "2016-06-17", + "2016-06-16", + "2016-06-15", + "2016-06-14", + "2016-06-13", + "2016-06-10", + "2016-06-09", + "2016-06-08", + "2016-06-07", + "2016-06-06", + "2016-06-03", + "2016-06-02", + "2016-06-01", + "2016-05-31", + "2016-05-27", + "2016-05-26", + "2016-05-25", + "2016-05-24", + "2016-05-23", + "2016-05-20", + "2016-05-19", + "2016-05-18", + "2016-05-17", + "2016-05-16", + "2016-05-13", + "2016-05-12", + "2016-05-11", + "2016-05-10", + "2016-05-09", + "2016-05-06", + "2016-05-05", + "2016-05-04", + "2016-05-03", + "2016-05-02", + "2016-04-29", + "2016-04-28", + "2016-04-27", + "2016-04-26", + "2016-04-25", + "2016-04-22", + "2016-04-21", + "2016-04-20", + "2016-04-19", + "2016-04-18", + "2016-04-15", + "2016-04-14", + "2016-04-13", + "2016-04-12", + "2016-04-11", + "2016-04-08", + "2016-04-07", + "2016-04-06", + "2016-04-05", + "2016-04-04", + "2016-04-01", + "2016-03-31", + "2016-03-30", + "2016-03-29", + "2016-03-28", + "2016-03-24", + "2016-03-23", + "2016-03-22", + "2016-03-21", + "2016-03-18", + "2016-03-17", + "2016-03-16", + "2016-03-15", + "2016-03-14", + "2016-03-11", + "2016-03-10", + "2016-03-09", + "2016-03-08", + "2016-03-07", + "2016-03-04", + "2016-03-03", + "2016-03-02", + "2016-03-01", + "2016-02-29", + "2016-02-26", + "2016-02-25", + "2016-02-24", + "2016-02-23", + "2016-02-22", + "2016-02-19", + "2016-02-18", + "2016-02-17", + "2016-02-16", + "2016-02-12", + "2016-02-11", + "2016-02-10", + "2016-02-09", + "2016-02-08", + "2016-02-05", + "2016-02-04", + "2016-02-03", + "2016-02-02", + "2016-02-01", + "2016-01-29", + "2016-01-28", + "2016-01-27", + "2016-01-26", + "2016-01-25", + "2016-01-22", + "2016-01-21", + "2016-01-20", + "2016-01-19", + "2016-01-15", + "2016-01-14", + "2016-01-13", + "2016-01-12", + "2016-01-11", + "2016-01-08", + "2016-01-07", + "2016-01-06", + "2016-01-05", + "2016-01-04" + ], + "open": [ + "776.330017", + "781.440002", + "777.849976", + "775.50", + "782.73999", + "786.590027", + "780.00", + "772.659973", + "769.00", + "772.419983", + "769.75", + "762.890015", + "759.609985", + "764.47998", + "755.130005", + "770.099976", + "778.590027", + "780.00", + "773.450012", + "773.01001", + "769.25", + "767.01001", + "769.330017", + "768.73999", + "769.00", + "767.00", + "770.580017", + "775.47998", + "773.27002", + "775.00", + "780.01001", + "777.320007", + "780.299988", + "783.75", + "781.50", + "785.00", + "783.75", + "781.099976", + "782.00", + "773.780029", + "772.219971", + "767.179993", + "768.690002", + "761.090027", + "772.710022", + "747.039978", + "738.280029", + "739.039978", + "740.669983", + "741.859985", + "740.359985", + "737.330017", + "729.890015", + "722.710022", + "725.72998", + "721.580017", + "723.619995", + "719.119995", + "708.049988", + "699.50", + "698.080017", + "689.97998", + "696.059998", + "692.200012", + "685.469971", + "683.00", + "678.969971", + "671.00", + "675.169983", + "697.450012", + "699.059998", + "698.400024", + "698.77002", + "708.650024", + "714.909973", + "719.00", + "716.47998", + "716.51001", + "719.469971", + "722.869995", + "723.960022", + "719.840027", + "724.909973", + "729.27002", + "732.50", + "734.530029", + "731.73999", + "724.01001", + "722.869995", + "720.76001", + "706.859985", + "706.530029", + "701.619995", + "702.359985", + "703.669983", + "715.98999", + "709.130005", + "711.929993", + "717.059998", + "723.409973", + "716.75", + "712.00", + "698.380005", + "697.700012", + "690.48999", + "696.869995", + "697.630005", + "690.700012", + "708.26001", + "707.289978", + "725.419983", + "716.099976", + "726.299988", + "755.380005", + "758.00", + "769.51001", + "760.460022", + "753.97998", + "754.01001", + "749.159973", + "738.00", + "743.02002", + "743.969971", + "745.369995", + "735.77002", + "738.00", + "750.059998", + "738.599976", + "749.25", + "750.099976", + "734.590027", + "736.789978", + "732.01001", + "742.359985", + "737.460022", + "736.50", + "741.859985", + "736.450012", + "726.369995", + "726.919983", + "726.809998", + "720.00", + "708.119995", + "698.469971", + "688.590027", + "706.900024", + "714.98999", + "718.679993", + "719.00", + "703.619995", + "700.320007", + "708.580017", + "700.01001", + "688.919983", + "701.450012", + "707.450012", + "695.030029", + "710.00", + "698.090027", + "692.97998", + "690.26001", + "675.00", + "686.859985", + "672.320007", + "667.849976", + "703.869995", + "722.809998", + "770.219971", + "784.50", + "750.460022", + "731.530029", + "722.219971", + "713.669983", + "713.849976", + "723.580017", + "723.599976", + "702.179993", + "688.609985", + "703.299988", + "692.289978", + "705.380005", + "730.849976", + "721.679993", + "716.609985", + "731.450012", + "730.309998", + "730.00", + "746.450012", + "743.00" + ], + "high": [ + "780.940002", + "785.799988", + "781.809998", + "785.98999", + "782.73999", + "788.929993", + "789.849976", + "777.159973", + "773.330017", + "774.00", + "769.75", + "773.799988", + "767.679993", + "766.219971", + "770.289978", + "773.244995", + "780.349976", + "782.72998", + "782.00", + "773.919983", + "771.02002", + "769.090027", + "774.466003", + "774.98999", + "776.080017", + "771.890015", + "774.50", + "776.440002", + "774.539978", + "777.099976", + "782.859985", + "780.809998", + "780.97998", + "787.48999", + "783.39502", + "789.75", + "786.812012", + "788.940002", + "782.630005", + "783.039978", + "774.070007", + "773.210022", + "775.840027", + "780.429993", + "778.549988", + "748.650024", + "744.460022", + "741.690002", + "742.609985", + "743.23999", + "741.690002", + "742.130005", + "736.98999", + "736.130005", + "725.73999", + "722.210022", + "724.00", + "722.940002", + "716.51001", + "705.710022", + "698.200012", + "701.679993", + "696.940002", + "700.650024", + "692.320007", + "687.429016", + "680.330017", + "672.299988", + "689.400024", + "701.950012", + "700.859985", + "702.77002", + "702.47998", + "708.820007", + "716.650024", + "722.97998", + "722.469971", + "725.440002", + "725.890015", + "729.539978", + "728.570007", + "721.97998", + "724.909973", + "729.48999", + "733.02002", + "737.210022", + "739.72998", + "733.935974", + "728.330017", + "727.51001", + "720.969971", + "711.478027", + "714.580017", + "706.00", + "711.599976", + "721.52002", + "718.47998", + "716.661987", + "719.25", + "724.47998", + "723.50", + "718.710022", + "711.859985", + "702.320007", + "699.75", + "697.840027", + "700.640015", + "697.619995", + "714.169983", + "708.97998", + "725.765991", + "723.929993", + "736.119995", + "760.450012", + "758.132019", + "769.900024", + "768.049988", + "761.00", + "757.309998", + "754.380005", + "743.830017", + "745.00", + "745.450012", + "747.00", + "746.23999", + "742.799988", + "752.799988", + "750.340027", + "750.849976", + "757.880005", + "747.25", + "738.98999", + "737.747009", + "745.719971", + "745.00", + "742.50", + "742.00", + "743.070007", + "737.469971", + "732.289978", + "735.50", + "726.919983", + "716.440002", + "705.679993", + "703.789978", + "708.091003", + "716.48999", + "719.450012", + "720.00", + "718.809998", + "710.890015", + "713.429993", + "705.97998", + "700.00", + "708.400024", + "713.23999", + "703.080994", + "712.349976", + "709.75", + "698.00", + "693.75", + "689.349976", + "701.309998", + "699.900024", + "684.030029", + "703.98999", + "727.00", + "774.50", + "789.869995", + "757.859985", + "744.98999", + "733.690002", + "718.234985", + "718.280029", + "729.679993", + "728.130005", + "719.190002", + "706.849976", + "709.97998", + "706.73999", + "721.924988", + "734.73999", + "728.75", + "718.85498", + "733.22998", + "738.50", + "747.179993", + "752.00", + "744.059998" + ], + "low": [ + "774.090027", + "774.231995", + "774.969971", + "774.307983", + "773.070007", + "784.150024", + "778.440002", + "768.301025", + "768.530029", + "764.440979", + "764.659973", + "759.960022", + "759.109985", + "755.799988", + "754.00", + "759.659973", + "773.580017", + "776.200012", + "771.00", + "768.409973", + "764.299988", + "765.380005", + "766.840027", + "766.61499", + "765.849976", + "763.184998", + "767.070007", + "771.784973", + "770.049988", + "773.130005", + "777.00", + "773.530029", + "773.44397", + "780.109985", + "780.400024", + "782.969971", + "782.778015", + "780.570007", + "778.091003", + "772.340027", + "768.794983", + "766.820007", + "767.849976", + "761.090027", + "766.77002", + "739.299988", + "737.00", + "734.27002", + "737.50", + "736.559998", + "735.830994", + "737.099976", + "729.00", + "721.190002", + "719.054993", + "718.030029", + "716.849976", + "715.909973", + "707.23999", + "696.434998", + "688.215027", + "689.090027", + "688.880005", + "692.130005", + "683.650024", + "681.409973", + "673.00", + "663.283997", + "673.450012", + "687.00", + "693.08197", + "692.01001", + "693.409973", + "688.452026", + "703.26001", + "717.309998", + "713.119995", + "716.51001", + "716.429993", + "722.335999", + "720.580017", + "716.549988", + "714.609985", + "720.559998", + "724.169983", + "730.659973", + "731.26001", + "724.00", + "720.280029", + "719.705017", + "706.859985", + "704.179993", + "700.52002", + "696.799988", + "700.630005", + "704.109985", + "705.650024", + "709.26001", + "709.00", + "712.799988", + "715.719971", + "710.00", + "698.106995", + "695.719971", + "689.01001", + "692.00", + "691.00", + "689.00", + "689.549988", + "692.36499", + "703.026001", + "715.590027", + "713.609985", + "749.549988", + "750.01001", + "749.330017", + "757.299988", + "752.69397", + "752.705017", + "744.260986", + "731.01001", + "736.049988", + "735.549988", + "736.280029", + "735.559998", + "735.369995", + "742.429993", + "737.00", + "740.940002", + "748.73999", + "728.76001", + "732.50", + "731.00", + "736.150024", + "737.460022", + "733.515991", + "731.830017", + "736.00", + "724.51001", + "724.77002", + "725.150024", + "717.125", + "703.359985", + "694.00", + "685.340027", + "686.900024", + "706.02002", + "706.02002", + "712.00", + "699.77002", + "697.679993", + "700.859985", + "690.585022", + "680.780029", + "693.580017", + "702.51001", + "694.049988", + "696.030029", + "691.380005", + "685.049988", + "678.599976", + "668.867981", + "682.130005", + "668.77002", + "663.059998", + "680.150024", + "701.859985", + "720.50", + "764.650024", + "743.27002", + "726.799988", + "712.349976", + "694.390015", + "706.47998", + "710.01001", + "720.120972", + "694.460022", + "673.26001", + "693.409973", + "685.369995", + "689.099976", + "698.609985", + "717.317017", + "703.539978", + "713.00", + "719.059998", + "728.919983", + "738.640015", + "731.257996" + ], + "close": [ + "777.289978", + "775.01001", + "781.559998", + "783.01001", + "774.210022", + "786.900024", + "787.210022", + "776.219971", + "771.409973", + "765.700012", + "768.880005", + "771.76001", + "762.48999", + "759.690002", + "769.02002", + "759.659973", + "775.320007", + "780.349976", + "780.080017", + "771.460022", + "768.780029", + "767.049988", + "769.090027", + "772.150024", + "769.539978", + "769.409973", + "769.640015", + "772.080017", + "772.150024", + "775.419983", + "777.50", + "779.909973", + "777.140015", + "782.440002", + "783.219971", + "784.849976", + "784.679993", + "784.26001", + "781.76001", + "782.219971", + "771.609985", + "773.179993", + "771.070007", + "772.880005", + "768.789978", + "745.909973", + "741.77002", + "738.419983", + "739.77002", + "742.73999", + "738.630005", + "741.190002", + "736.960022", + "733.780029", + "719.849976", + "720.950012", + "716.97998", + "720.640015", + "715.090027", + "705.630005", + "695.359985", + "697.77002", + "694.950012", + "699.210022", + "692.099976", + "684.109985", + "680.039978", + "668.26001", + "675.219971", + "701.869995", + "697.460022", + "695.940002", + "693.710022", + "691.719971", + "710.359985", + "718.919983", + "718.27002", + "718.359985", + "719.409973", + "728.580017", + "728.280029", + "716.650024", + "716.549988", + "722.340027", + "730.400024", + "734.150024", + "735.719971", + "732.659973", + "724.119995", + "725.27002", + "720.090027", + "704.23999", + "709.73999", + "700.320007", + "706.630005", + "706.22998", + "716.48999", + "710.830017", + "713.309998", + "715.289978", + "723.179993", + "712.900024", + "711.119995", + "701.429993", + "695.700012", + "692.359985", + "698.210022", + "693.01001", + "691.02002", + "705.840027", + "708.140015", + "723.150024", + "718.77002", + "759.140015", + "752.669983", + "753.929993", + "766.609985", + "759.00", + "753.200012", + "751.719971", + "743.090027", + "736.099976", + "739.150024", + "740.280029", + "745.690002", + "737.799988", + "745.289978", + "749.909973", + "744.950012", + "750.530029", + "744.77002", + "733.530029", + "735.299988", + "738.059998", + "740.75", + "742.090027", + "737.599976", + "737.780029", + "736.090027", + "728.330017", + "730.48999", + "726.820007", + "712.820007", + "705.23999", + "693.969971", + "695.159973", + "710.890015", + "712.419983", + "718.849976", + "718.809998", + "697.77002", + "705.070007", + "705.75", + "699.559998", + "695.849976", + "706.460022", + "700.909973", + "697.349976", + "708.400024", + "691.00", + "682.400024", + "683.109985", + "684.119995", + "678.109985", + "682.73999", + "683.570007", + "708.01001", + "726.950012", + "764.650024", + "752.00", + "742.950012", + "730.960022", + "699.98999", + "713.039978", + "711.669983", + "725.25", + "706.590027", + "698.450012", + "701.789978", + "694.450012", + "714.719971", + "700.559998", + "726.070007", + "716.030029", + "714.469971", + "726.390015", + "743.619995", + "742.580017", + "741.840027" + ], + "increasing": { + "line": { + "color": "#4dac26" + }, + "name": "ABC" + }, + "decreasing": { + "line": { + "color": "#d01c8b" + }, + "showlegend": false + } + }, + { + "type": "candlestick", + "name": "XYZ", + "x": [ + "2016-09-30", + "2016-09-29", + "2016-09-28", + "2016-09-27", + "2016-09-26", + "2016-09-23", + "2016-09-22", + "2016-09-21", + "2016-09-20", + "2016-09-19", + "2016-09-16", + "2016-09-15", + "2016-09-14", + "2016-09-13", + "2016-09-12", + "2016-09-09", + "2016-09-08", + "2016-09-07", + "2016-09-06", + "2016-09-02", + "2016-09-01", + "2016-08-31", + "2016-08-30", + "2016-08-29", + "2016-08-26", + "2016-08-25", + "2016-08-24", + "2016-08-23", + "2016-08-22", + "2016-08-19", + "2016-08-18", + "2016-08-17", + "2016-08-16", + "2016-08-15", + "2016-08-12", + "2016-08-11", + "2016-08-10", + "2016-08-09", + "2016-08-08", + "2016-08-05", + "2016-08-04", + "2016-08-03", + "2016-08-02", + "2016-08-01", + "2016-07-29", + "2016-07-28", + "2016-07-27", + "2016-07-26", + "2016-07-25", + "2016-07-22", + "2016-07-21", + "2016-07-20", + "2016-07-19", + "2016-07-18", + "2016-07-15", + "2016-07-14", + "2016-07-13", + "2016-07-12", + "2016-07-11", + "2016-07-08", + "2016-07-07", + "2016-07-06", + "2016-07-05", + "2016-07-01", + "2016-06-30", + "2016-06-29", + "2016-06-28", + "2016-06-27", + "2016-06-24", + "2016-06-23", + "2016-06-22", + "2016-06-21", + "2016-06-20", + "2016-06-17", + "2016-06-16", + "2016-06-15", + "2016-06-14", + "2016-06-13", + "2016-06-10", + "2016-06-09", + "2016-06-08", + "2016-06-07", + "2016-06-06", + "2016-06-03", + "2016-06-02", + "2016-06-01", + "2016-05-31", + "2016-05-27", + "2016-05-26", + "2016-05-25", + "2016-05-24", + "2016-05-23", + "2016-05-20", + "2016-05-19", + "2016-05-18", + "2016-05-17", + "2016-05-16", + "2016-05-13", + "2016-05-12", + "2016-05-11", + "2016-05-10", + "2016-05-09", + "2016-05-06", + "2016-05-05", + "2016-05-04", + "2016-05-03", + "2016-05-02", + "2016-04-29", + "2016-04-28", + "2016-04-27", + "2016-04-26", + "2016-04-25", + "2016-04-22", + "2016-04-21", + "2016-04-20", + "2016-04-19", + "2016-04-18", + "2016-04-15", + "2016-04-14", + "2016-04-13", + "2016-04-12", + "2016-04-11", + "2016-04-08", + "2016-04-07", + "2016-04-06", + "2016-04-05", + "2016-04-04", + "2016-04-01", + "2016-03-31", + "2016-03-30", + "2016-03-29", + "2016-03-28", + "2016-03-24", + "2016-03-23", + "2016-03-22", + "2016-03-21", + "2016-03-18", + "2016-03-17", + "2016-03-16", + "2016-03-15", + "2016-03-14", + "2016-03-11", + "2016-03-10", + "2016-03-09", + "2016-03-08", + "2016-03-07", + "2016-03-04", + "2016-03-03", + "2016-03-02", + "2016-03-01", + "2016-02-29", + "2016-02-26", + "2016-02-25", + "2016-02-24", + "2016-02-23", + "2016-02-22", + "2016-02-19", + "2016-02-18", + "2016-02-17", + "2016-02-16", + "2016-02-12", + "2016-02-11", + "2016-02-10", + "2016-02-09", + "2016-02-08", + "2016-02-05", + "2016-02-04", + "2016-02-03", + "2016-02-02", + "2016-02-01", + "2016-01-29", + "2016-01-28", + "2016-01-27", + "2016-01-26", + "2016-01-25", + "2016-01-22", + "2016-01-21", + "2016-01-20", + "2016-01-19", + "2016-01-15", + "2016-01-14", + "2016-01-13", + "2016-01-12", + "2016-01-11", + "2016-01-08", + "2016-01-07", + "2016-01-06", + "2016-01-05", + "2016-01-04" + ], + "open": [ + "112.459999", + "113.160004", + "113.690002", + "113.00", + "111.639999", + "114.419998", + "114.349998", + "113.849998", + "113.050003", + "115.190002", + "115.120003", + "113.860001", + "108.730003", + "107.510002", + "102.650002", + "104.639999", + "107.25", + "107.830002", + "107.900002", + "107.699997", + "106.139999", + "105.660004", + "105.800003", + "106.620003", + "107.410004", + "107.389999", + "108.57", + "108.589996", + "108.860001", + "108.769997", + "109.230003", + "109.099998", + "109.629997", + "108.139999", + "107.779999", + "108.519997", + "108.709999", + "108.230003", + "107.519997", + "106.269997", + "105.580002", + "104.809998", + "106.050003", + "104.410004", + "104.190002", + "102.830002", + "104.269997", + "96.82", + "98.25", + "99.260002", + "99.830002", + "100.00", + "99.559998", + "98.699997", + "98.919998", + "97.389999", + "97.410004", + "97.169998", + "96.75", + "96.489998", + "95.699997", + "94.599998", + "95.389999", + "95.489998", + "94.440002", + "93.970001", + "92.900002", + "93.00", + "92.910004", + "95.940002", + "96.25", + "94.940002", + "96.00", + "96.620003", + "96.449997", + "97.82", + "97.32", + "98.690002", + "98.529999", + "98.50", + "99.019997", + "99.25", + "97.989998", + "97.790001", + "97.599998", + "99.019997", + "99.599998", + "99.440002", + "99.68", + "98.669998", + "97.220001", + "95.870003", + "94.639999", + "94.639999", + "94.160004", + "94.550003", + "92.389999", + "90.00", + "92.720001", + "93.480003", + "93.330002", + "93.00", + "93.370003", + "94.00", + "95.199997", + "94.199997", + "93.970001", + "93.989998", + "97.610001", + "96.00", + "103.910004", + "105.00", + "105.010002", + "106.93", + "106.639999", + "107.879997", + "108.889999", + "112.110001", + "111.620003", + "110.800003", + "109.339996", + "108.970001", + "108.910004", + "109.949997", + "110.230003", + "109.510002", + "110.419998", + "108.779999", + "109.720001", + "108.650002", + "104.889999", + "106.00", + "105.470001", + "106.480003", + "105.25", + "105.93", + "106.339996", + "105.519997", + "104.610001", + "103.959999", + "101.910004", + "102.239998", + "101.410004", + "101.309998", + "100.779999", + "102.389999", + "102.370003", + "100.580002", + "100.510002", + "97.650002", + "96.860001", + "97.199997", + "96.050003", + "93.980003", + "96.400002", + "96.309998", + "96.00", + "98.839996", + "96.669998", + "95.019997", + "94.190002", + "93.790001", + "95.919998", + "94.290001", + "93.129997", + "96.519997", + "95.860001", + "95.00", + "95.419998", + "96.470001", + "94.790001", + "93.790001", + "96.040001", + "99.93", + "101.519997", + "98.629997", + "97.059998", + "95.099998", + "98.410004", + "96.199997", + "97.959999", + "100.32", + "100.550003", + "98.970001", + "98.550003", + "98.68", + "100.559998", + "105.75", + "102.610001" + ], + "high": [ + "113.370003", + "113.800003", + "114.639999", + "113.18", + "113.389999", + "114.790001", + "114.940002", + "113.989998", + "114.120003", + "116.18", + "116.129997", + "115.730003", + "113.029999", + "108.790001", + "105.720001", + "105.720001", + "107.269997", + "108.760002", + "108.300003", + "108.00", + "106.800003", + "106.57", + "106.50", + "107.440002", + "107.949997", + "107.879997", + "108.75", + "109.32", + "109.099998", + "109.690002", + "109.599998", + "109.370003", + "110.230003", + "109.540001", + "108.440002", + "108.93", + "108.900002", + "108.940002", + "108.370003", + "107.650002", + "106.00", + "105.839996", + "106.07", + "106.150002", + "104.550003", + "104.449997", + "104.349998", + "97.970001", + "98.839996", + "99.300003", + "101.00", + "100.459999", + "100.00", + "100.129997", + "99.300003", + "98.989998", + "97.669998", + "97.699997", + "97.650002", + "96.889999", + "96.50", + "95.660004", + "95.400002", + "96.470001", + "95.769997", + "94.550003", + "93.660004", + "93.050003", + "94.660004", + "96.290001", + "96.889999", + "96.349998", + "96.57", + "96.650002", + "97.75", + "98.410004", + "98.480003", + "99.120003", + "99.349998", + "99.989998", + "99.559998", + "99.870003", + "101.889999", + "98.269997", + "97.839996", + "99.540001", + "100.400002", + "100.470001", + "100.730003", + "99.739998", + "98.089996", + "97.190002", + "95.43", + "94.639999", + "95.209999", + "94.699997", + "94.389999", + "91.669998", + "92.779999", + "93.57", + "93.57", + "93.769997", + "93.449997", + "94.07", + "95.900002", + "95.739998", + "94.080002", + "94.720001", + "97.879997", + "98.709999", + "105.300003", + "105.650002", + "106.480003", + "106.93", + "108.089996", + "108.00", + "108.949997", + "112.300003", + "112.389999", + "112.339996", + "110.50", + "110.610001", + "109.769997", + "110.419998", + "110.980003", + "110.730003", + "112.190002", + "110.00", + "109.900002", + "110.419998", + "107.790001", + "106.190002", + "106.25", + "107.07", + "107.290001", + "107.650002", + "106.50", + "106.470001", + "106.309998", + "105.18", + "102.910004", + "102.279999", + "102.239998", + "101.580002", + "101.760002", + "102.830002", + "103.75", + "101.709999", + "100.889999", + "100.769997", + "98.230003", + "98.019997", + "96.760002", + "96.379997", + "96.50", + "96.900002", + "96.760002", + "98.889999", + "98.209999", + "96.849998", + "94.50", + "94.720001", + "96.349998", + "95.940002", + "95.699997", + "96.919998", + "97.330002", + "96.839996", + "96.040001", + "96.709999", + "97.339996", + "94.519997", + "96.629997", + "100.879997", + "101.529999", + "101.459999", + "97.879997", + "98.190002", + "98.650002", + "97.709999", + "100.480003", + "101.190002", + "100.690002", + "99.059998", + "99.110001", + "100.129997", + "102.370003", + "105.849998", + "105.370003" + ], + "low": [ + "111.800003", + "111.800003", + "113.43", + "112.339996", + "111.550003", + "111.550003", + "114.00", + "112.440002", + "112.510002", + "113.25", + "114.040001", + "113.489998", + "108.599998", + "107.239998", + "102.529999", + "103.129997", + "105.239998", + "107.07", + "107.510002", + "106.82", + "105.620003", + "105.639999", + "105.50", + "106.290001", + "106.309998", + "106.68", + "107.68", + "108.529999", + "107.849998", + "108.360001", + "109.019997", + "108.339996", + "109.209999", + "108.080002", + "107.779999", + "107.849998", + "107.760002", + "108.010002", + "107.160004", + "106.18", + "105.279999", + "104.769997", + "104.00", + "104.410004", + "103.68", + "102.82", + "102.75", + "96.419998", + "96.919998", + "98.309998", + "99.129997", + "99.739998", + "99.339996", + "98.599998", + "98.50", + "97.32", + "96.839996", + "97.120003", + "96.730003", + "96.050003", + "95.620003", + "94.370003", + "94.459999", + "95.330002", + "94.300003", + "93.629997", + "92.139999", + "91.50", + "92.650002", + "95.25", + "95.349998", + "94.68", + "95.029999", + "95.300003", + "96.07", + "97.029999", + "96.75", + "97.099998", + "98.480003", + "98.459999", + "98.68", + "98.959999", + "97.550003", + "97.449997", + "96.629997", + "98.330002", + "98.82", + "99.25", + "98.639999", + "98.110001", + "96.839996", + "95.669998", + "94.519997", + "93.57", + "93.889999", + "93.010002", + "91.650002", + "90.00", + "89.470001", + "92.459999", + "92.110001", + "92.589996", + "91.849998", + "92.68", + "93.82", + "93.68", + "92.400002", + "92.510002", + "94.25", + "95.68", + "103.910004", + "104.510002", + "104.620003", + "105.519997", + "106.059998", + "106.230003", + "106.940002", + "109.730003", + "111.330002", + "110.800003", + "108.660004", + "108.830002", + "108.169998", + "108.120003", + "109.199997", + "109.419998", + "110.269997", + "108.199997", + "108.879997", + "108.599998", + "104.879997", + "105.059998", + "104.889999", + "105.900002", + "105.209999", + "105.139999", + "105.190002", + "104.959999", + "104.589996", + "103.849998", + "101.779999", + "101.50", + "100.150002", + "100.269997", + "100.400002", + "100.959999", + "101.370003", + "100.449997", + "99.639999", + "97.419998", + "96.650002", + "96.580002", + "95.25", + "93.32", + "94.550003", + "95.919998", + "95.800003", + "96.089996", + "96.150002", + "94.610001", + "93.010002", + "92.589996", + "94.099998", + "93.93", + "93.040001", + "93.690002", + "95.190002", + "94.080002", + "94.279999", + "95.400002", + "94.349998", + "92.389999", + "93.339996", + "98.07", + "99.209999", + "98.370003", + "94.940002", + "93.419998", + "95.50", + "95.360001", + "95.739998", + "97.300003", + "98.839996", + "97.339996", + "96.760002", + "96.43", + "99.870003", + "102.410004", + "102.00" + ], + "close": [ + "113.050003", + "112.18", + "113.949997", + "113.089996", + "112.879997", + "112.709999", + "114.620003", + "113.550003", + "113.57", + "113.580002", + "114.919998", + "115.57", + "111.769997", + "107.949997", + "105.440002", + "103.129997", + "105.519997", + "108.360001", + "107.699997", + "107.730003", + "106.730003", + "106.099998", + "106.00", + "106.82", + "106.940002", + "107.57", + "108.029999", + "108.849998", + "108.510002", + "109.360001", + "109.080002", + "109.220001", + "109.379997", + "109.480003", + "108.18", + "107.93", + "108.00", + "108.809998", + "108.370003", + "107.480003", + "105.870003", + "105.790001", + "104.480003", + "106.050003", + "104.209999", + "104.339996", + "102.949997", + "96.669998", + "97.339996", + "98.660004", + "99.43", + "99.959999", + "99.870003", + "99.830002", + "98.779999", + "98.790001", + "96.870003", + "97.419998", + "96.980003", + "96.68", + "95.940002", + "95.529999", + "94.989998", + "95.889999", + "95.599998", + "94.400002", + "93.589996", + "92.040001", + "93.400002", + "96.099998", + "95.550003", + "95.910004", + "95.099998", + "95.330002", + "97.550003", + "97.139999", + "97.459999", + "97.339996", + "98.830002", + "99.650002", + "98.940002", + "99.029999", + "98.629997", + "97.919998", + "97.720001", + "98.459999", + "99.860001", + "100.349998", + "100.410004", + "99.620003", + "97.900002", + "96.43", + "95.220001", + "94.199997", + "94.559998", + "93.489998", + "93.879997", + "90.519997", + "90.339996", + "92.510002", + "93.419998", + "92.790001", + "92.720001", + "93.239998", + "94.190002", + "95.18", + "93.639999", + "93.739998", + "94.830002", + "97.82", + "104.349998", + "105.080002", + "105.68", + "105.970001", + "107.129997", + "106.910004", + "107.480003", + "109.849998", + "112.099998", + "112.040001", + "110.440002", + "109.019997", + "108.660004", + "108.540001", + "110.959999", + "109.809998", + "111.120003", + "109.989998", + "108.989998", + "109.559998", + "107.68", + "105.190002", + "105.669998", + "106.129997", + "106.720001", + "105.910004", + "105.919998", + "105.800003", + "105.970001", + "104.580002", + "102.519997", + "102.260002", + "101.169998", + "101.120003", + "101.029999", + "101.870003", + "103.010002", + "101.50", + "100.75", + "100.529999", + "96.690002", + "96.910004", + "96.760002", + "96.099998", + "94.690002", + "96.879997", + "96.040001", + "96.260002", + "98.120003", + "96.639999", + "93.989998", + "93.699997", + "94.269997", + "94.989998", + "95.010002", + "94.019997", + "96.599998", + "96.349998", + "94.480003", + "96.43", + "97.339996", + "94.089996", + "93.419998", + "99.989998", + "99.440002", + "101.419998", + "96.300003", + "96.790001", + "96.660004", + "97.129997", + "99.519997", + "97.389999", + "99.959999", + "98.529999", + "96.959999", + "96.449997", + "100.699997", + "102.709999", + "105.349998" + ], + "increasing": { + "line": { + "color": "#b8e186" + }, + "name": "XYZ" + }, + "decreasing": { + "line": { + "color": "#f1b6da" + }, + "showlegend": false + }, + "yaxis": "y2" + } + ], + "layout": { + "xaxis": { + "range": [ + 1452453861917, + 1471240462978 + ] + }, + "yaxis": { + "tickprefix": "$", + "showtickprefix": "last" + }, + "yaxis2": { + "overlaying": "y", + "side": "right", + "tickprefix": "$", + "showtickprefix": "last" + }, + "legend": { + "x": 0, + "xanchor": "left", + "y": 1, + "yanchor": "bottom" + }, + "height": 600, + "width": 1100 + } +} diff --git a/test/image/mocks/finance_style.json b/test/image/mocks/finance_style.json new file mode 100644 index 00000000000..c7fb15e7def --- /dev/null +++ b/test/image/mocks/finance_style.json @@ -0,0 +1,228 @@ +{ + "data": [ + { + "type": "ohlc", + "open": [ + 20.209909078315253, + 20.971458192536797, + 20.351637601564537, + 20.638787230238137, + 20.801207851356043, + 20.259400168418665, + 20.142546114709678, + 20.350481052458303, + 20.41237904058586, + 20.783078255505295, + 20.490837840387382, + 20.244009924485404, + 20.171651413925517, + 20.27146182932616, + 20.33826775209861, + 20.291279762863333, + 20.886620393408265, + 20.422687422471526, + 20.579767391892602, + 20.577853105114748 + ], + "high": [ + 21.282352659705946, + 21.80039935401815, + 21.089537739749208, + 21.458006854607813, + 21.45005601769593, + 21.267041591419993, + 21.301758579927615, + 21.198538628011864, + 21.292791856779516, + 21.57661801130053, + 21.421548824830023, + 21.041424941601797, + 21.236699171014312, + 21.27159841736843, + 21.918646868299803, + 21.191142086578246, + 21.58460796322318, + 21.580154750208806, + 21.96911073992379, + 21.20676528483314 + ], + "low": [ + 19.166795412593174, + 19.279658608486525, + 19.37402923741759, + 19.065260737441236, + 19.1800075208827, + 19.117474838928064, + 19.954902315389273, + 19.237903553876222, + 19.753163769806367, + 19.9469940900036, + 19.962987438067096, + 19.456007410577016, + 19.60888046562783, + 19.286884095632068, + 19.19108946202958, + 19.384679515021254, + 19.989347894799064, + 19.3781138384352, + 19.309287076745818, + 19.755579576495684 + ], + "close": [ + 20.39162164923735, + 20.861695663049506, + 20.29278072395081, + 20.96669764859506, + 20.219098866544158, + 20.928776644426442, + 20.99407238754678, + 20.046341836023863, + 20.21039924078742, + 20.946522272678727, + 20.120801482863463, + 20.31792491216739, + 20.30504503296301, + 20.565476958831894, + 20.85790378618509, + 20.32158387968238, + 20.7040288083194, + 20.374060562211536, + 20.609242660033345, + 20.9832297017119 + ], + "increasing": { + "line": { + "color": "rgb(150, 200, 250)" + } + }, + "decreasing": { + "line": { + "color": "rgb(128, 128, 128)", + "dash": "dash" + } + }, + "line": { + "width": 3 + }, + "tickwidth": 0.2 + }, + { + "type": "candlestick", + "open": [ + 25.853458179392945, + 25.07616990956719, + 25.65124333859165, + 25.48414021344863, + 25.056496862387217, + 25.809829475241305, + 25.827540062997834, + 25.324598504321568, + 25.968582089525757, + 25.963981380629363, + 25.50477114012293, + 25.864579556482603, + 25.291556901078152, + 25.506302688462558, + 25.973269631805568, + 25.07482258950527, + 25.778746613691958, + 25.584465908282777, + 25.70963747743168, + 25.689722592070876 + ], + "high": [ + 26.485005723474284, + 26.636147719832294, + 26.197422776766363, + 26.00895494268842, + 26.96755702751034, + 26.391642879657034, + 26.01747844412544, + 26.48949192184998, + 26.358868761393314, + 26.737067009462333, + 26.939764079398152, + 26.251475519829782, + 26.171922022898766, + 26.961884613679263, + 26.825362946253517, + 26.99227451066661, + 26.242392931731345, + 26.88687290949724, + 26.415791597955774, + 26.55335528041219 + ], + "low": [ + 24.246492786607476, + 24.913586962777618, + 24.03362745129435, + 24.77998098602382, + 24.225496241002414, + 24.267434499813042, + 24.54509560839949, + 24.16946017408691, + 24.37465918734527, + 24.007039222535273, + 24.0273825692926, + 24.027390639108862, + 24.461601813698984, + 24.182820955230333, + 24.970999371172905, + 24.506663751142867, + 24.880080413036524, + 24.031332563917474, + 24.98987648841576, + 24.37745016804795 + ], + "close": [ + 25.807359510055747, + 25.271040318548362, + 25.270764777180606, + 25.949049688936388, + 25.28272319483142, + 25.130455345098206, + 25.379055011804887, + 25.224254555209544, + 25.681480742436882, + 25.24894261431543, + 25.601206842520657, + 25.62137592881503, + 25.85919540251596, + 25.948933037384897, + 25.016925230484166, + 25.337655806066646, + 25.136689245624723, + 25.748766825352625, + 25.398644940922246, + 25.093220170315078 + ], + "increasing": { + "line": { + "color": "#d3d3d3" + }, + "fillcolor": "rgb(150, 200, 250)" + }, + "decreasing": { + "line": { + "color": "#d3d3d3" + }, + "fillcolor": "rgb(128, 128, 128)" + }, + "line": { + "width": 2 + }, + "whiskerwidth": 0.5 + } + ], + "layout": { + "xaxis": { + "rangeslider": { + "visible": false + } + }, + "showlegend": false, + "height": 450, + "width": 1100, + "autosize": true + } +} diff --git a/test/image/mocks/ohlc_first.json b/test/image/mocks/ohlc_first.json new file mode 100644 index 00000000000..b0625071788 --- /dev/null +++ b/test/image/mocks/ohlc_first.json @@ -0,0 +1,983 @@ +{ + "data": [ + { + "type": "ohlc", + "name": "Company A", + "x": [ + "2016-09-30", + "2016-09-29", + "2016-09-28", + "2016-09-27", + "2016-09-26", + "2016-09-23", + "2016-09-22", + "2016-09-21", + "2016-09-20", + "2016-09-19", + "2016-09-16", + "2016-09-15", + "2016-09-14", + "2016-09-13", + "2016-09-12", + "2016-09-09", + "2016-09-08", + "2016-09-07", + "2016-09-06", + "2016-09-02", + "2016-09-01", + "2016-08-31", + "2016-08-30", + "2016-08-29", + "2016-08-26", + "2016-08-25", + "2016-08-24", + "2016-08-23", + "2016-08-22", + "2016-08-19", + "2016-08-18", + "2016-08-17", + "2016-08-16", + "2016-08-15", + "2016-08-12", + "2016-08-11", + "2016-08-10", + "2016-08-09", + "2016-08-08", + "2016-08-05", + "2016-08-04", + "2016-08-03", + "2016-08-02", + "2016-08-01", + "2016-07-29", + "2016-07-28", + "2016-07-27", + "2016-07-26", + "2016-07-25", + "2016-07-22", + "2016-07-21", + "2016-07-20", + "2016-07-19", + "2016-07-18", + "2016-07-15", + "2016-07-14", + "2016-07-13", + "2016-07-12", + "2016-07-11", + "2016-07-08", + "2016-07-07", + "2016-07-06", + "2016-07-05", + "2016-07-01", + "2016-06-30", + "2016-06-29", + "2016-06-28", + "2016-06-27", + "2016-06-24", + "2016-06-23", + "2016-06-22", + "2016-06-21", + "2016-06-20", + "2016-06-17", + "2016-06-16", + "2016-06-15", + "2016-06-14", + "2016-06-13", + "2016-06-10", + "2016-06-09", + "2016-06-08", + "2016-06-07", + "2016-06-06", + "2016-06-03", + "2016-06-02", + "2016-06-01", + "2016-05-31", + "2016-05-27", + "2016-05-26", + "2016-05-25", + "2016-05-24", + "2016-05-23", + "2016-05-20", + "2016-05-19", + "2016-05-18", + "2016-05-17", + "2016-05-16", + "2016-05-13", + "2016-05-12", + "2016-05-11", + "2016-05-10", + "2016-05-09", + "2016-05-06", + "2016-05-05", + "2016-05-04", + "2016-05-03", + "2016-05-02", + "2016-04-29", + "2016-04-28", + "2016-04-27", + "2016-04-26", + "2016-04-25", + "2016-04-22", + "2016-04-21", + "2016-04-20", + "2016-04-19", + "2016-04-18", + "2016-04-15", + "2016-04-14", + "2016-04-13", + "2016-04-12", + "2016-04-11", + "2016-04-08", + "2016-04-07", + "2016-04-06", + "2016-04-05", + "2016-04-04", + "2016-04-01", + "2016-03-31", + "2016-03-30", + "2016-03-29", + "2016-03-28", + "2016-03-24", + "2016-03-23", + "2016-03-22", + "2016-03-21", + "2016-03-18", + "2016-03-17", + "2016-03-16", + "2016-03-15", + "2016-03-14", + "2016-03-11", + "2016-03-10", + "2016-03-09", + "2016-03-08", + "2016-03-07", + "2016-03-04", + "2016-03-03", + "2016-03-02", + "2016-03-01", + "2016-02-29", + "2016-02-26", + "2016-02-25", + "2016-02-24", + "2016-02-23", + "2016-02-22", + "2016-02-19", + "2016-02-18", + "2016-02-17", + "2016-02-16", + "2016-02-12", + "2016-02-11", + "2016-02-10", + "2016-02-09", + "2016-02-08", + "2016-02-05", + "2016-02-04", + "2016-02-03", + "2016-02-02", + "2016-02-01", + "2016-01-29", + "2016-01-28", + "2016-01-27", + "2016-01-26", + "2016-01-25", + "2016-01-22", + "2016-01-21", + "2016-01-20", + "2016-01-19", + "2016-01-15", + "2016-01-14", + "2016-01-13", + "2016-01-12", + "2016-01-11", + "2016-01-08", + "2016-01-07", + "2016-01-06", + "2016-01-05", + "2016-01-04" + ], + "open": [ + "776.330017", + "781.440002", + "777.849976", + "775.50", + "782.73999", + "786.590027", + "780.00", + "772.659973", + "769.00", + "772.419983", + "769.75", + "762.890015", + "759.609985", + "764.47998", + "755.130005", + "770.099976", + "778.590027", + "780.00", + "773.450012", + "773.01001", + "769.25", + "767.01001", + "769.330017", + "768.73999", + "769.00", + "767.00", + "770.580017", + "775.47998", + "773.27002", + "775.00", + "780.01001", + "777.320007", + "780.299988", + "783.75", + "781.50", + "785.00", + "783.75", + "781.099976", + "782.00", + "773.780029", + "772.219971", + "767.179993", + "768.690002", + "761.090027", + "772.710022", + "747.039978", + "738.280029", + "739.039978", + "740.669983", + "741.859985", + "740.359985", + "737.330017", + "729.890015", + "722.710022", + "725.72998", + "721.580017", + "723.619995", + "719.119995", + "708.049988", + "699.50", + "698.080017", + "689.97998", + "696.059998", + "692.200012", + "685.469971", + "683.00", + "678.969971", + "671.00", + "675.169983", + "697.450012", + "699.059998", + "698.400024", + "698.77002", + "708.650024", + "714.909973", + "719.00", + "716.47998", + "716.51001", + "719.469971", + "722.869995", + "723.960022", + "719.840027", + "724.909973", + "729.27002", + "732.50", + "734.530029", + "731.73999", + "724.01001", + "722.869995", + "720.76001", + "706.859985", + "706.530029", + "701.619995", + "702.359985", + "703.669983", + "715.98999", + "709.130005", + "711.929993", + "717.059998", + "723.409973", + "716.75", + "712.00", + "698.380005", + "697.700012", + "690.48999", + "696.869995", + "697.630005", + "690.700012", + "708.26001", + "707.289978", + "725.419983", + "716.099976", + "726.299988", + "755.380005", + "758.00", + "769.51001", + "760.460022", + "753.97998", + "754.01001", + "749.159973", + "738.00", + "743.02002", + "743.969971", + "745.369995", + "735.77002", + "738.00", + "750.059998", + "738.599976", + "749.25", + "750.099976", + "734.590027", + "736.789978", + "732.01001", + "742.359985", + "737.460022", + "736.50", + "741.859985", + "736.450012", + "726.369995", + "726.919983", + "726.809998", + "720.00", + "708.119995", + "698.469971", + "688.590027", + "706.900024", + "714.98999", + "718.679993", + "719.00", + "703.619995", + "700.320007", + "708.580017", + "700.01001", + "688.919983", + "701.450012", + "707.450012", + "695.030029", + "710.00", + "698.090027", + "692.97998", + "690.26001", + "675.00", + "686.859985", + "672.320007", + "667.849976", + "703.869995", + "722.809998", + "770.219971", + "784.50", + "750.460022", + "731.530029", + "722.219971", + "713.669983", + "713.849976", + "723.580017", + "723.599976", + "702.179993", + "688.609985", + "703.299988", + "692.289978", + "705.380005", + "730.849976", + "721.679993", + "716.609985", + "731.450012", + "730.309998", + "730.00", + "746.450012", + "743.00" + ], + "high": [ + "780.940002", + "785.799988", + "781.809998", + "785.98999", + "782.73999", + "788.929993", + "789.849976", + "777.159973", + "773.330017", + "774.00", + "769.75", + "773.799988", + "767.679993", + "766.219971", + "770.289978", + "773.244995", + "780.349976", + "782.72998", + "782.00", + "773.919983", + "771.02002", + "769.090027", + "774.466003", + "774.98999", + "776.080017", + "771.890015", + "774.50", + "776.440002", + "774.539978", + "777.099976", + "782.859985", + "780.809998", + "780.97998", + "787.48999", + "783.39502", + "789.75", + "786.812012", + "788.940002", + "782.630005", + "783.039978", + "774.070007", + "773.210022", + "775.840027", + "780.429993", + "778.549988", + "748.650024", + "744.460022", + "741.690002", + "742.609985", + "743.23999", + "741.690002", + "742.130005", + "736.98999", + "736.130005", + "725.73999", + "722.210022", + "724.00", + "722.940002", + "716.51001", + "705.710022", + "698.200012", + "701.679993", + "696.940002", + "700.650024", + "692.320007", + "687.429016", + "680.330017", + "672.299988", + "689.400024", + "701.950012", + "700.859985", + "702.77002", + "702.47998", + "708.820007", + "716.650024", + "722.97998", + "722.469971", + "725.440002", + "725.890015", + "729.539978", + "728.570007", + "721.97998", + "724.909973", + "729.48999", + "733.02002", + "737.210022", + "739.72998", + "733.935974", + "728.330017", + "727.51001", + "720.969971", + "711.478027", + "714.580017", + "706.00", + "711.599976", + "721.52002", + "718.47998", + "716.661987", + "719.25", + "724.47998", + "723.50", + "718.710022", + "711.859985", + "702.320007", + "699.75", + "697.840027", + "700.640015", + "697.619995", + "714.169983", + "708.97998", + "725.765991", + "723.929993", + "736.119995", + "760.450012", + "758.132019", + "769.900024", + "768.049988", + "761.00", + "757.309998", + "754.380005", + "743.830017", + "745.00", + "745.450012", + "747.00", + "746.23999", + "742.799988", + "752.799988", + "750.340027", + "750.849976", + "757.880005", + "747.25", + "738.98999", + "737.747009", + "745.719971", + "745.00", + "742.50", + "742.00", + "743.070007", + "737.469971", + "732.289978", + "735.50", + "726.919983", + "716.440002", + "705.679993", + "703.789978", + "708.091003", + "716.48999", + "719.450012", + "720.00", + "718.809998", + "710.890015", + "713.429993", + "705.97998", + "700.00", + "708.400024", + "713.23999", + "703.080994", + "712.349976", + "709.75", + "698.00", + "693.75", + "689.349976", + "701.309998", + "699.900024", + "684.030029", + "703.98999", + "727.00", + "774.50", + "789.869995", + "757.859985", + "744.98999", + "733.690002", + "718.234985", + "718.280029", + "729.679993", + "728.130005", + "719.190002", + "706.849976", + "709.97998", + "706.73999", + "721.924988", + "734.73999", + "728.75", + "718.85498", + "733.22998", + "738.50", + "747.179993", + "752.00", + "744.059998" + ], + "low": [ + "774.090027", + "774.231995", + "774.969971", + "774.307983", + "773.070007", + "784.150024", + "778.440002", + "768.301025", + "768.530029", + "764.440979", + "764.659973", + "759.960022", + "759.109985", + "755.799988", + "754.00", + "759.659973", + "773.580017", + "776.200012", + "771.00", + "768.409973", + "764.299988", + "765.380005", + "766.840027", + "766.61499", + "765.849976", + "763.184998", + "767.070007", + "771.784973", + "770.049988", + "773.130005", + "777.00", + "773.530029", + "773.44397", + "780.109985", + "780.400024", + "782.969971", + "782.778015", + "780.570007", + "778.091003", + "772.340027", + "768.794983", + "766.820007", + "767.849976", + "761.090027", + "766.77002", + "739.299988", + "737.00", + "734.27002", + "737.50", + "736.559998", + "735.830994", + "737.099976", + "729.00", + "721.190002", + "719.054993", + "718.030029", + "716.849976", + "715.909973", + "707.23999", + "696.434998", + "688.215027", + "689.090027", + "688.880005", + "692.130005", + "683.650024", + "681.409973", + "673.00", + "663.283997", + "673.450012", + "687.00", + "693.08197", + "692.01001", + "693.409973", + "688.452026", + "703.26001", + "717.309998", + "713.119995", + "716.51001", + "716.429993", + "722.335999", + "720.580017", + "716.549988", + "714.609985", + "720.559998", + "724.169983", + "730.659973", + "731.26001", + "724.00", + "720.280029", + "719.705017", + "706.859985", + "704.179993", + "700.52002", + "696.799988", + "700.630005", + "704.109985", + "705.650024", + "709.26001", + "709.00", + "712.799988", + "715.719971", + "710.00", + "698.106995", + "695.719971", + "689.01001", + "692.00", + "691.00", + "689.00", + "689.549988", + "692.36499", + "703.026001", + "715.590027", + "713.609985", + "749.549988", + "750.01001", + "749.330017", + "757.299988", + "752.69397", + "752.705017", + "744.260986", + "731.01001", + "736.049988", + "735.549988", + "736.280029", + "735.559998", + "735.369995", + "742.429993", + "737.00", + "740.940002", + "748.73999", + "728.76001", + "732.50", + "731.00", + "736.150024", + "737.460022", + "733.515991", + "731.830017", + "736.00", + "724.51001", + "724.77002", + "725.150024", + "717.125", + "703.359985", + "694.00", + "685.340027", + "686.900024", + "706.02002", + "706.02002", + "712.00", + "699.77002", + "697.679993", + "700.859985", + "690.585022", + "680.780029", + "693.580017", + "702.51001", + "694.049988", + "696.030029", + "691.380005", + "685.049988", + "678.599976", + "668.867981", + "682.130005", + "668.77002", + "663.059998", + "680.150024", + "701.859985", + "720.50", + "764.650024", + "743.27002", + "726.799988", + "712.349976", + "694.390015", + "706.47998", + "710.01001", + "720.120972", + "694.460022", + "673.26001", + "693.409973", + "685.369995", + "689.099976", + "698.609985", + "717.317017", + "703.539978", + "713.00", + "719.059998", + "728.919983", + "738.640015", + "731.257996" + ], + "close": [ + "777.289978", + "775.01001", + "781.559998", + "783.01001", + "774.210022", + "786.900024", + "787.210022", + "776.219971", + "771.409973", + "765.700012", + "768.880005", + "771.76001", + "762.48999", + "759.690002", + "769.02002", + "759.659973", + "775.320007", + "780.349976", + "780.080017", + "771.460022", + "768.780029", + "767.049988", + "769.090027", + "772.150024", + "769.539978", + "769.409973", + "769.640015", + "772.080017", + "772.150024", + "775.419983", + "777.50", + "779.909973", + "777.140015", + "782.440002", + "783.219971", + "784.849976", + "784.679993", + "784.26001", + "781.76001", + "782.219971", + "771.609985", + "773.179993", + "771.070007", + "772.880005", + "768.789978", + "745.909973", + "741.77002", + "738.419983", + "739.77002", + "742.73999", + "738.630005", + "741.190002", + "736.960022", + "733.780029", + "719.849976", + "720.950012", + "716.97998", + "720.640015", + "715.090027", + "705.630005", + "695.359985", + "697.77002", + "694.950012", + "699.210022", + "692.099976", + "684.109985", + "680.039978", + "668.26001", + "675.219971", + "701.869995", + "697.460022", + "695.940002", + "693.710022", + "691.719971", + "710.359985", + "718.919983", + "718.27002", + "718.359985", + "719.409973", + "728.580017", + "728.280029", + "716.650024", + "716.549988", + "722.340027", + "730.400024", + "734.150024", + "735.719971", + "732.659973", + "724.119995", + "725.27002", + "720.090027", + "704.23999", + "709.73999", + "700.320007", + "706.630005", + "706.22998", + "716.48999", + "710.830017", + "713.309998", + "715.289978", + "723.179993", + "712.900024", + "711.119995", + "701.429993", + "695.700012", + "692.359985", + "698.210022", + "693.01001", + "691.02002", + "705.840027", + "708.140015", + "723.150024", + "718.77002", + "759.140015", + "752.669983", + "753.929993", + "766.609985", + "759.00", + "753.200012", + "751.719971", + "743.090027", + "736.099976", + "739.150024", + "740.280029", + "745.690002", + "737.799988", + "745.289978", + "749.909973", + "744.950012", + "750.530029", + "744.77002", + "733.530029", + "735.299988", + "738.059998", + "740.75", + "742.090027", + "737.599976", + "737.780029", + "736.090027", + "728.330017", + "730.48999", + "726.820007", + "712.820007", + "705.23999", + "693.969971", + "695.159973", + "710.890015", + "712.419983", + "718.849976", + "718.809998", + "697.77002", + "705.070007", + "705.75", + "699.559998", + "695.849976", + "706.460022", + "700.909973", + "697.349976", + "708.400024", + "691.00", + "682.400024", + "683.109985", + "684.119995", + "678.109985", + "682.73999", + "683.570007", + "708.01001", + "726.950012", + "764.650024", + "752.00", + "742.950012", + "730.960022", + "699.98999", + "713.039978", + "711.669983", + "725.25", + "706.590027", + "698.450012", + "701.789978", + "694.450012", + "714.719971", + "700.559998", + "726.070007", + "716.030029", + "714.469971", + "726.390015", + "743.619995", + "742.580017", + "741.840027" + ] + } + ], + "layout": { + "xaxis": { + "range": [ + 1472577187875, + 1475225280000 + ] + }, + "yaxis": { + "tickprefix": "$", + "showtickprefix": "last" + }, + "legend": { + "x": 0, + "xanchor": "left", + "y": 1, + "yanchor": "bottom" + }, + "height": 600, + "width": 1100 + } +} diff --git a/test/jasmine/bundle_tests/finance_test.js b/test/jasmine/bundle_tests/finance_test.js new file mode 100644 index 00000000000..b56e10e14b6 --- /dev/null +++ b/test/jasmine/bundle_tests/finance_test.js @@ -0,0 +1,41 @@ +var Plotly = require('@lib/core'); +var ohlc = require('@lib/ohlc'); +var candlestick = require('@lib/candlestick'); + +var d3 = require('d3'); +var createGraphDiv = require('../assets/create_graph_div'); +var destroyGraphDiv = require('../assets/destroy_graph_div'); + +describe('Bundle with finance trace type', function() { + 'use strict'; + + Plotly.register([ohlc, candlestick]); + + var mock = require('@mocks/finance_style.json'); + + it('should register the correct trace modules for the generated traces', function() { + var transformModules = Object.keys(Plotly.Plots.transformsRegistry); + + expect(transformModules).toEqual(['ohlc', 'candlestick']); + }); + + it('should register the correct trace modules for the generated traces', function() { + var traceModules = Object.keys(Plotly.Plots.modules); + + expect(traceModules).toEqual(['scatter', 'box', 'ohlc', 'candlestick']); + }); + + it('should graph ohlc and candlestick traces', function(done) { + + Plotly.plot(createGraphDiv(), mock.data, mock.layout).then(function() { + var gSubplot = d3.select('g.cartesianlayer'); + + expect(gSubplot.selectAll('g.trace.scatter').size()).toEqual(2); + expect(gSubplot.selectAll('g.trace.boxes').size()).toEqual(2); + + destroyGraphDiv(); + done(); + }); + + }); +}); diff --git a/test/jasmine/tests/finance_test.js b/test/jasmine/tests/finance_test.js new file mode 100644 index 00000000000..d0aa516eade --- /dev/null +++ b/test/jasmine/tests/finance_test.js @@ -0,0 +1,783 @@ +var Plotly = require('@lib/index'); +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 mock0 = { + open: [33.01, 33.31, 33.50, 32.06, 34.12, 33.05, 33.31, 33.50], + high: [34.20, 34.37, 33.62, 34.25, 35.18, 33.25, 35.37, 34.62], + low: [31.70, 30.75, 32.87, 31.62, 30.81, 32.75, 32.75, 32.87], + close: [34.10, 31.93, 33.37, 33.18, 31.18, 33.10, 32.93, 33.70] +}; + +var mock1 = Lib.extendDeep({}, mock0, { + x: [ + '2016-09-01', '2016-09-02', '2016-09-03', '2016-09-04', + '2016-09-05', '2016-09-06', '2016-09-07', '2016-09-10' + ] +}); + +describe('finance charts defaults:', function() { + 'use strict'; + + function _supply(data, layout) { + var gd = { + data: data, + layout: layout + }; + + Plots.supplyDefaults(gd); + + return gd; + } + + it('should generated the correct number of full traces', function() { + var trace0 = Lib.extendDeep({}, mock0, { + type: 'ohlc' + }); + + var trace1 = Lib.extendDeep({}, mock1, { + type: 'candlestick' + }); + + var out = _supply([trace0, trace1]); + + expect(out.data.length).toEqual(2); + expect(out._fullData.length).toEqual(4); + + var directions = out._fullData.map(function(fullTrace) { + return fullTrace.transforms[0].direction; + }); + + expect(directions).toEqual(['increasing', 'decreasing', 'increasing', 'decreasing']); + }); + + it('should not mutate user data', function() { + var trace0 = Lib.extendDeep({}, mock0, { + type: 'ohlc' + }); + + var trace1 = Lib.extendDeep({}, mock1, { + type: 'candlestick' + }); + + var out = _supply([trace0, trace1]); + expect(out.data[0]).toBe(trace0); + expect(out.data[0].transforms).toBeUndefined(); + expect(out.data[1]).toBe(trace1); + expect(out.data[1].transforms).toBeUndefined(); + + // ... and in an idempotent way + + var out2 = _supply(out.data); + expect(out2.data[0]).toBe(trace0); + expect(out2.data[0].transforms).toBeUndefined(); + expect(out2.data[1]).toBe(trace1); + expect(out2.data[1].transforms).toBeUndefined(); + }); + + it('should work with transforms', function() { + var trace0 = Lib.extendDeep({}, mock1, { + type: 'ohlc', + transforms: [{ + type: 'filter' + }] + }); + + var trace1 = Lib.extendDeep({}, mock0, { + type: 'candlestick', + transforms: [{ + type: 'filter' + }] + }); + + var out = _supply([trace0, trace1]); + + expect(out.data.length).toEqual(2); + expect(out._fullData.length).toEqual(4); + + var transformTypesIn = out.data.map(function(trace) { + return trace.transforms.map(function(opts) { + return opts.type; + }); + }); + + expect(transformTypesIn).toEqual([ ['filter'], ['filter'] ]); + + var transformTypesOut = out._fullData.map(function(fullTrace) { + return fullTrace.transforms.map(function(opts) { + return opts.type; + }); + }); + + // dummy 'ohlc' and 'candlestick' transforms are pushed at the end + // of the 'transforms' array container + + expect(transformTypesOut).toEqual([ + ['filter', 'ohlc'], ['filter', 'ohlc'], + ['filter', 'candlestick'], ['filter', 'candlestick'] + ]); + }); + + it('should slice data array according to minimum supplied length', function() { + + function assertDataLength(fullTrace, len) { + expect(fullTrace.visible).toBe(true); + + expect(fullTrace.open.length).toEqual(len); + expect(fullTrace.high.length).toEqual(len); + expect(fullTrace.low.length).toEqual(len); + expect(fullTrace.close.length).toEqual(len); + } + + var trace0 = Lib.extendDeep({}, mock0, { type: 'ohlc' }); + trace0.open = [33.01, 33.31, 33.50, 32.06, 34.12]; + + var trace1 = Lib.extendDeep({}, mock1, { type: 'candlestick' }); + trace1.x = ['2016-09-01', '2016-09-02', '2016-09-03', '2016-09-04']; + + var out = _supply([trace0, trace1]); + + assertDataLength(out._fullData[0], 5); + assertDataLength(out._fullData[1], 5); + assertDataLength(out._fullData[2], 4); + assertDataLength(out._fullData[3], 4); + + expect(out._fullData[0]._fullInput.x).toBeUndefined(); + expect(out._fullData[1]._fullInput.x).toBeUndefined(); + expect(out._fullData[2]._fullInput.x.length).toEqual(4); + expect(out._fullData[3]._fullInput.x.length).toEqual(4); + }); + + it('should set visible to *false* when minimum supplied length is 0', function() { + var trace0 = Lib.extendDeep({}, mock0, { type: 'ohlc' }); + trace0.close = undefined; + + var trace1 = Lib.extendDeep({}, mock1, { type: 'candlestick' }); + trace1.high = null; + + var out = _supply([trace0, trace1]); + + expect(out.data.length).toEqual(2); + expect(out._fullData.length).toEqual(4); + + var visibilities = out._fullData.map(function(fullTrace) { + return fullTrace.visible; + }); + + expect(visibilities).toEqual([false, false, false, false]); + }); + + it('direction *showlegend* should be inherited from trace-wide *showlegend*', function() { + var trace0 = Lib.extendDeep({}, mock0, { + type: 'ohlc', + showlegend: false, + }); + + var trace1 = Lib.extendDeep({}, mock1, { + type: 'candlestick', + showlegend: false, + increasing: { showlegend: true }, + decreasing: { showlegend: true } + }); + + var out = _supply([trace0, trace1]); + + var visibilities = out._fullData.map(function(fullTrace) { + return fullTrace.showlegend; + }); + + expect(visibilities).toEqual([false, false, false, false]); + }); + + it('direction *name* should be inherited from trace-wide *name*', function() { + var trace0 = Lib.extendDeep({}, mock0, { + type: 'ohlc', + name: 'Company A' + }); + + var trace1 = Lib.extendDeep({}, mock1, { + type: 'candlestick', + name: 'Company B', + increasing: { name: 'B - UP' }, + decreasing: { name: 'B - DOWN' } + }); + + var out = _supply([trace0, trace1]); + + var names = out._fullData.map(function(fullTrace) { + return fullTrace.name; + }); + + expect(names).toEqual([ + 'Company A - increasing', + 'Company A - decreasing', + 'B - UP', + 'B - DOWN' + ]); + }); + + it('trace-wide styling should set default for corresponding per-direction styling', function() { + function assertLine(cont, width, dash) { + expect(cont.line.width).toEqual(width); + if(dash) expect(cont.line.dash).toEqual(dash); + } + + var trace0 = Lib.extendDeep({}, mock0, { + type: 'ohlc', + line: { width: 1, dash: 'dash' }, + decreasing: { line: { dash: 'dot' } } + }); + + var trace1 = Lib.extendDeep({}, mock1, { + type: 'candlestick', + line: { width: 3 }, + increasing: { line: { width: 0 } } + }); + + var out = _supply([trace0, trace1]); + + + var fullData = out._fullData; + var fullInput = fullData.map(function(fullTrace) { return fullTrace._fullInput; }); + + assertLine(fullInput[0].increasing, 1, 'dash'); + assertLine(fullInput[0].decreasing, 1, 'dot'); + assertLine(fullInput[2].increasing, 0); + assertLine(fullInput[2].decreasing, 3); + + assertLine(fullData[0], 1, 'dash'); + assertLine(fullData[1], 1, 'dot'); + assertLine(fullData[2], 0); + assertLine(fullData[3], 3); + }); + + it('trace-wide *visible* should be passed to generated traces', function() { + var trace0 = Lib.extendDeep({}, mock0, { + type: 'ohlc', + visible: 'legendonly' + }); + + var trace1 = Lib.extendDeep({}, mock1, { + type: 'candlestick', + visible: false + }); + + var out = _supply([trace0, trace1]); + + var visibilities = out._fullData.map(function(fullTrace) { + return fullTrace.visible; + }); + + // only three items here as visible: false traces are not transformed + + expect(visibilities).toEqual(['legendonly', 'legendonly', false]); + }); + + it('should add a few layout settings by default', function() { + var trace0 = Lib.extendDeep({}, mock0, { + type: 'ohlc' + }); + + var layout0 = {}; + + var out0 = _supply([trace0], layout0); + + expect(out0.layout.xaxis.rangeslider).toBeDefined(); + expect(out0._fullLayout.xaxis.rangeslider.visible).toBe(true); + + var trace1 = Lib.extendDeep({}, mock0, { + type: 'candlestick' + }); + + var layout1 = { + xaxis: { rangeslider: { visible: false }} + }; + + var out1 = _supply([trace1], layout1); + + expect(out1.layout.xaxis.rangeslider).toBeDefined(); + expect(out1._fullLayout.xaxis.rangeslider.visible).toBe(false); + }); +}); + +describe('finance charts calc transforms:', function() { + 'use strict'; + + function calcDatatoTrace(calcTrace) { + return calcTrace[0].trace; + } + + function _calc(data, layout) { + var gd = { + data: data, + layout: layout || {} + }; + + Plots.supplyDefaults(gd); + Plots.doCalcdata(gd); + + return gd.calcdata.map(calcDatatoTrace); + } + + function ms2DateTime(v) { + return typeof v === 'number' ? Lib.ms2DateTime(v) : null; + } + + it('should fill when *x* is not present', function() { + var trace0 = Lib.extendDeep({}, mock0, { + type: 'ohlc', + }); + + var trace1 = Lib.extendDeep({}, mock0, { + type: 'candlestick', + }); + + var out = _calc([trace0, trace1]); + + expect(out[0].x).toEqual([ + -0.3, 0, 0, 0, 0, 0.3, null, + 2.7, 3, 3, 3, 3, 3.3, null, + 4.7, 5, 5, 5, 5, 5.3, null, + 6.7, 7, 7, 7, 7, 7.3, null + ]); + expect(out[1].x).toEqual([ + 0.7, 1, 1, 1, 1, 1.3, null, + 1.7, 2, 2, 2, 2, 2.3, null, + 3.7, 4, 4, 4, 4, 4.3, null, + 5.7, 6, 6, 6, 6, 6.3, null + ]); + expect(out[2].x).toEqual([ + 0, 0, 0, 0, 0, 0, + 3, 3, 3, 3, 3, 3, + 5, 5, 5, 5, 5, 5, + 7, 7, 7, 7, 7, 7 + ]); + expect(out[3].x).toEqual([ + 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, + 4, 4, 4, 4, 4, 4, + 6, 6, 6, 6, 6, 6 + ]); + }); + + it('should fill *text* for OHLC hover labels', function() { + var trace0 = Lib.extendDeep({}, mock0, { + type: 'ohlc', + text: ['A', 'B', 'C', 'D'] + }); + + var trace1 = Lib.extendDeep({}, mock1, { + type: 'ohlc', + text: 'IMPORTANT', + hoverinfo: 'x+text', + xaxis: 'x2' + }); + + var trace2 = Lib.extendDeep({}, mock1, { + type: 'ohlc', + hoverinfo: 'y', + xaxis: 'x2' + }); + + var trace3 = Lib.extendDeep({}, mock0, { + type: 'ohlc', + hoverinfo: 'x', + }); + + var out = _calc([trace0, trace1, trace2, trace3]); + + expect(out[0].hoverinfo).toEqual('x+text'); + expect(out[0].text[0]) + .toEqual('Open: 33.01
High: 34.2
Low: 31.7
Close: 34.1
A'); + expect(out[0].hoverinfo).toEqual('x+text'); + expect(out[1].text[0]) + .toEqual('Open: 33.31
High: 34.37
Low: 30.75
Close: 31.93
B'); + + expect(out[2].hoverinfo).toEqual('x+text'); + expect(out[2].text[0]).toEqual('IMPORTANT'); + + expect(out[3].hoverinfo).toEqual('x+text'); + expect(out[3].text[0]).toEqual('IMPORTANT'); + + expect(out[4].hoverinfo).toEqual('text'); + expect(out[4].text[0]) + .toEqual('Open: 33.01
High: 34.2
Low: 31.7
Close: 34.1'); + expect(out[5].hoverinfo).toEqual('text'); + expect(out[5].text[0]) + .toEqual('Open: 33.31
High: 34.37
Low: 30.75
Close: 31.93'); + + expect(out[6].hoverinfo).toEqual('x'); + expect(out[6].text[0]).toEqual(''); + expect(out[7].hoverinfo).toEqual('x'); + expect(out[7].text[0]).toEqual(''); + }); + + it('should work with *filter* transforms', function() { + var trace0 = Lib.extendDeep({}, mock1, { + type: 'ohlc', + tickwidth: 0.05, + transforms: [{ + type: 'filter', + operation: '>', + filtersrc: 'open', + value: 33 + }] + }); + + var trace1 = Lib.extendDeep({}, mock1, { + type: 'candlestick', + transforms: [{ + type: 'filter', + operation: '{}', + filtersrc: 'x', + value: ['2016-09-01', '2016-09-10'] + }] + }); + + var out = _calc([trace0, trace1]); + + expect(out.length).toEqual(4); + + expect(out[0].x.map(ms2DateTime)).toEqual([ + '2016-08-31 22:48', '2016-09-01', '2016-09-01', '2016-09-01', '2016-09-01', '2016-09-01 01:12', null, + '2016-09-05 22:48', '2016-09-06', '2016-09-06', '2016-09-06', '2016-09-06', '2016-09-06 01:12', null, + '2016-09-09 22:48', '2016-09-10', '2016-09-10', '2016-09-10', '2016-09-10', '2016-09-10 01:12', null + ]); + expect(out[0].y).toEqual([ + 33.01, 33.01, 34.2, 31.7, 34.1, 34.1, null, + 33.05, 33.05, 33.25, 32.75, 33.1, 33.1, null, + 33.5, 33.5, 34.62, 32.87, 33.7, 33.7, null + ]); + expect(out[1].x.map(ms2DateTime)).toEqual([ + '2016-09-01 22:48', '2016-09-02', '2016-09-02', '2016-09-02', '2016-09-02', '2016-09-02 01:12', null, + '2016-09-02 22:48', '2016-09-03', '2016-09-03', '2016-09-03', '2016-09-03', '2016-09-03 01:12', null, + '2016-09-04 22:48', '2016-09-05', '2016-09-05', '2016-09-05', '2016-09-05', '2016-09-05 01:12', null, + '2016-09-06 22:48', '2016-09-07', '2016-09-07', '2016-09-07', '2016-09-07', '2016-09-07 01:12', null + ]); + expect(out[1].y).toEqual([ + 33.31, 33.31, 34.37, 30.75, 31.93, 31.93, null, + 33.5, 33.5, 33.62, 32.87, 33.37, 33.37, null, + 34.12, 34.12, 35.18, 30.81, 31.18, 31.18, null, + 33.31, 33.31, 35.37, 32.75, 32.93, 32.93, null + ]); + + expect(out[2].x).toEqual([ + '2016-09-01', '2016-09-01', '2016-09-01', '2016-09-01', '2016-09-01', '2016-09-01', + '2016-09-10', '2016-09-10', '2016-09-10', '2016-09-10', '2016-09-10', '2016-09-10' + ]); + expect(out[2].y).toEqual([ + 31.7, 33.01, 34.1, 34.1, 34.1, 34.2, + 32.87, 33.5, 33.7, 33.7, 33.7, 34.62 + ]); + + expect(out[3].x).toEqual([]); + expect(out[3].y).toEqual([]); + }); + + it('should work with *groupby* transforms (ohlc)', function() { + var opts = { + type: 'groupby', + groups: ['b', 'b', 'b', 'a'], + }; + + var trace0 = Lib.extendDeep({}, mock1, { + type: 'ohlc', + tickwidth: 0.05, + transforms: [opts] + }); + + var out = _calc([trace0]); + + expect(out[0].name).toEqual('trace 0 - increasing'); + expect(out[0].x.map(ms2DateTime)).toEqual([ + '2016-08-31 22:48', '2016-09-01', '2016-09-01', '2016-09-01', '2016-09-01', '2016-09-01 01:12', null, + ]); + expect(out[0].y).toEqual([ + 33.01, 33.01, 34.2, 31.7, 34.1, 34.1, null, + ]); + + expect(out[1].name).toEqual('trace 0 - decreasing'); + expect(out[1].x.map(ms2DateTime)).toEqual([ + '2016-09-01 22:48', '2016-09-02', '2016-09-02', '2016-09-02', '2016-09-02', '2016-09-02 01:12', null, + '2016-09-02 22:48', '2016-09-03', '2016-09-03', '2016-09-03', '2016-09-03', '2016-09-03 01:12', null + ]); + expect(out[1].y).toEqual([ + 33.31, 33.31, 34.37, 30.75, 31.93, 31.93, null, + 33.5, 33.5, 33.62, 32.87, 33.37, 33.37, null + ]); + + expect(out[2].name).toEqual('trace 0 - increasing'); + expect(out[2].x.map(ms2DateTime)).toEqual([ + '2016-09-03 23:59:59.999', '2016-09-04', '2016-09-04', '2016-09-04', '2016-09-04', '2016-09-04', null + ]); + expect(out[2].y).toEqual([ + 32.06, 32.06, 34.25, 31.62, 33.18, 33.18, null + ]); + + expect(out[3].name).toEqual('trace 0 - decreasing'); + expect(out[3].x.map(ms2DateTime)).toEqual([]); + expect(out[3].y).toEqual([]); + }); + + it('should work with *groupby* transforms (candlestick)', function() { + var opts = { + type: 'groupby', + groups: ['a', 'b', 'b', 'a'], + }; + + var trace0 = Lib.extendDeep({}, mock1, { + type: 'candlestick', + transforms: [opts] + }); + + var out = _calc([trace0]); + + expect(out[0].name).toEqual('trace 0 - increasing'); + expect(out[0].x).toEqual([ + '2016-09-01', '2016-09-01', '2016-09-01', '2016-09-01', '2016-09-01', '2016-09-01', + '2016-09-04', '2016-09-04', '2016-09-04', '2016-09-04', '2016-09-04', '2016-09-04' + ]); + expect(out[0].y).toEqual([ + 31.7, 33.01, 34.1, 34.1, 34.1, 34.2, + 31.62, 32.06, 33.18, 33.18, 33.18, 34.25 + ]); + + expect(out[1].name).toEqual('trace 0 - decreasing'); + expect(out[1].x).toEqual([]); + expect(out[1].y).toEqual([]); + + expect(out[2].name).toEqual('trace 0 - increasing'); + expect(out[2].x).toEqual([]); + expect(out[2].y).toEqual([]); + + expect(out[3].name).toEqual('trace 0 - decreasing'); + expect(out[3].x).toEqual([ + '2016-09-02', '2016-09-02', '2016-09-02', '2016-09-02', '2016-09-02', '2016-09-02', + '2016-09-03', '2016-09-03', '2016-09-03', '2016-09-03', '2016-09-03', '2016-09-03' + ]); + expect(out[3].y).toEqual([ + 30.75, 33.31, 31.93, 31.93, 31.93, 34.37, + 32.87, 33.5, 33.37, 33.37, 33.37, 33.62 + ]); + }); +}); + +describe('finance charts updates:', function() { + 'use strict'; + + var gd; + + beforeEach(function() { + gd = createGraphDiv(); + }); + + afterEach(function() { + Plotly.purge(gd); + destroyGraphDiv(); + }); + + function countScatterTraces() { + return d3.select('g.cartesianlayer').selectAll('g.trace.scatter').size(); + } + + function countBoxTraces() { + return d3.select('g.cartesianlayer').selectAll('g.trace.boxes').size(); + } + + function countRangeSliders() { + return d3.select('g.rangeslider-rangeplot').size(); + } + + it('Plotly.restyle should work', function(done) { + var trace0 = Lib.extendDeep({}, mock0, { type: 'ohlc' }); + + var path0; + + Plotly.plot(gd, [trace0]).then(function() { + expect(gd.calcdata[0][0].x).toEqual(-0.3); + expect(gd.calcdata[0][0].y).toEqual(33.01); + + return Plotly.restyle(gd, 'tickwidth', 0.5); + }) + .then(function() { + expect(gd.calcdata[0][0].x).toEqual(-0.5); + + return Plotly.restyle(gd, 'open', [[0, 30.75, 32.87, 31.62, 30.81, 32.75, 32.75, 32.87]]); + }) + .then(function() { + expect(gd.calcdata[0][0].y).toEqual(0); + + return Plotly.restyle(gd, { + type: 'candlestick', + open: [[33.01, 33.31, 33.50, 32.06, 34.12, 33.05, 33.31, 33.50]] + }); + }) + .then(function() { + path0 = d3.select('path.box').attr('d'); + + return Plotly.restyle(gd, 'whiskerwidth', 0.2); + }) + .then(function() { + expect(d3.select('path.box').attr('d')).not.toEqual(path0); + + done(); + }); + + }); + + it('should be able to toggle visibility', function(done) { + var data = [ + Lib.extendDeep({}, mock0, { type: 'ohlc' }), + Lib.extendDeep({}, mock0, { type: 'candlestick' }), + ]; + + Plotly.plot(gd, data).then(function() { + expect(countScatterTraces()).toEqual(2); + expect(countBoxTraces()).toEqual(2); + + return Plotly.restyle(gd, 'visible', false); + }) + .then(function() { + expect(countScatterTraces()).toEqual(0); + expect(countBoxTraces()).toEqual(0); + + return Plotly.restyle(gd, 'visible', 'legendonly', [1]); + }) + .then(function() { + expect(countScatterTraces()).toEqual(0); + expect(countBoxTraces()).toEqual(0); + + return Plotly.restyle(gd, 'visible', true, [1]); + }) + .then(function() { + expect(countScatterTraces()).toEqual(0); + expect(countBoxTraces()).toEqual(2); + + return Plotly.restyle(gd, 'visible', true, [0]); + }) + .then(function() { + expect(countScatterTraces()).toEqual(2); + expect(countBoxTraces()).toEqual(2); + + return Plotly.restyle(gd, 'visible', 'legendonly', [0]); + }) + .then(function() { + expect(countScatterTraces()).toEqual(0); + expect(countBoxTraces()).toEqual(2); + + return Plotly.restyle(gd, 'visible', true); + }) + .then(function() { + expect(countScatterTraces()).toEqual(2); + expect(countBoxTraces()).toEqual(2); + + done(); + }); + }); + + it('Plotly.relayout should work', function(done) { + var trace0 = Lib.extendDeep({}, mock0, { type: 'ohlc' }); + + Plotly.plot(gd, [trace0]).then(function() { + expect(countRangeSliders()).toEqual(1); + + return Plotly.relayout(gd, 'xaxis.rangeslider.visible', false); + }) + .then(function() { + expect(countRangeSliders()).toEqual(0); + + done(); + }); + + }); + + it('Plotly.extendTraces should work', function(done) { + var data = [ + Lib.extendDeep({}, mock0, { type: 'ohlc' }), + Lib.extendDeep({}, mock0, { type: 'candlestick' }), + ]; + + // ohlc have 7 calc pts per 'x' coords + + Plotly.plot(gd, data).then(function() { + expect(gd.calcdata[0].length).toEqual(28); + expect(gd.calcdata[1].length).toEqual(28); + expect(gd.calcdata[2].length).toEqual(4); + expect(gd.calcdata[3].length).toEqual(4); + + return Plotly.extendTraces(gd, { + open: [[ 34, 35 ]], + high: [[ 40, 41 ]], + low: [[ 32, 33 ]], + close: [[ 38, 39 ]] + }, [1]); + }) + .then(function() { + expect(gd.calcdata[0].length).toEqual(28); + expect(gd.calcdata[1].length).toEqual(28); + expect(gd.calcdata[2].length).toEqual(6); + expect(gd.calcdata[3].length).toEqual(4); + + return Plotly.extendTraces(gd, { + open: [[ 34, 35 ]], + high: [[ 40, 41 ]], + low: [[ 32, 33 ]], + close: [[ 38, 39 ]] + }, [0]); + }) + .then(function() { + expect(gd.calcdata[0].length).toEqual(42); + expect(gd.calcdata[1].length).toEqual(28); + expect(gd.calcdata[2].length).toEqual(6); + expect(gd.calcdata[3].length).toEqual(4); + + done(); + }); + }); + + it('Plotly.deleteTraces / addTraces should work', function(done) { + var data = [ + Lib.extendDeep({}, mock0, { type: 'ohlc' }), + Lib.extendDeep({}, mock0, { type: 'candlestick' }), + ]; + + Plotly.plot(gd, data).then(function() { + expect(countScatterTraces()).toEqual(2); + expect(countBoxTraces()).toEqual(2); + + return Plotly.deleteTraces(gd, [1]); + }) + .then(function() { + expect(countScatterTraces()).toEqual(2); + expect(countBoxTraces()).toEqual(0); + + return Plotly.deleteTraces(gd, [0]); + }) + .then(function() { + expect(countScatterTraces()).toEqual(0); + expect(countBoxTraces()).toEqual(0); + + var trace = Lib.extendDeep({}, mock0, { type: 'candlestick' }); + + return Plotly.addTraces(gd, [trace]); + }) + .then(function() { + expect(countScatterTraces()).toEqual(0); + expect(countBoxTraces()).toEqual(2); + + var trace = Lib.extendDeep({}, mock0, { type: 'ohlc' }); + + return Plotly.addTraces(gd, [trace]); + }) + .then(function() { + expect(countScatterTraces()).toEqual(2); + expect(countBoxTraces()).toEqual(2); + + done(); + }); + }); +}); diff --git a/test/jasmine/tests/lib_test.js b/test/jasmine/tests/lib_test.js index 68dbf8e544b..2ed3fccf402 100644 --- a/test/jasmine/tests/lib_test.js +++ b/test/jasmine/tests/lib_test.js @@ -1367,6 +1367,37 @@ describe('Test lib.js:', function() { }); }); + describe('filterUnique', function() { + + it('should return array containing unique values', function() { + expect( + Lib.filterUnique(['a', 'a', 'b', 'b']) + ) + .toEqual(['a', 'b']); + + expect( + Lib.filterUnique(['1', ['1'], 1]) + ) + .toEqual(['1']); + + expect( + Lib.filterUnique([1, '1', [1]]) + ) + .toEqual([1]); + + expect( + Lib.filterUnique([ { a: 1 }, { b: 2 }]) + ) + .toEqual([{ a: 1 }]); + + expect( + Lib.filterUnique([null, undefined, null, null, undefined]) + ) + .toEqual([null, undefined]); + }); + + }); + describe('numSeparate', function() { it('should work on numbers and strings', function() { diff --git a/test/jasmine/tests/transform_multi_test.js b/test/jasmine/tests/transform_multi_test.js index 70ac4f4ed7b..43499b73079 100644 --- a/test/jasmine/tests/transform_multi_test.js +++ b/test/jasmine/tests/transform_multi_test.js @@ -157,11 +157,12 @@ describe('user-defined transforms:', function() { transforms: [transformIn] }]; - var layout = {}; + var layout = {}, + fullLayout = {}; function assertSupplyDefaultsArgs(_transformIn, traceOut, _layout) { expect(_transformIn).toBe(transformIn); - expect(_layout).toBe(layout); + expect(_layout).toBe(fullLayout); return transformOut; } @@ -171,6 +172,7 @@ describe('user-defined transforms:', function() { expect(opts.transform).toBe(transformOut); expect(opts.fullTrace._input).toBe(dataIn[0]); expect(opts.layout).toBe(layout); + expect(opts.fullLayout).toBe(fullLayout); return dataOut; } @@ -184,7 +186,7 @@ describe('user-defined transforms:', function() { }; Plotly.register(fakeTransformModule); - Plots.supplyDataDefaults(dataIn, [], layout); + Plots.supplyDataDefaults(dataIn, [], layout, fullLayout); delete Plots.transformsRegistry.fake; });