diff --git a/src/lib/index.js b/src/lib/index.js index 97a376d30b2..da1e75093ba 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -459,13 +459,25 @@ lib.noneOrAll = function(containerIn, containerOut, attrList) { * @param {object} cd : calcdata trace * @param {string} cdAttr : calcdata key */ -lib.mergeArray = function(traceAttr, cd, cdAttr) { +lib.mergeArray = function(traceAttr, cd, cdAttr, fn) { + var hasFn = typeof fn === 'function'; if(lib.isArrayOrTypedArray(traceAttr)) { var imax = Math.min(traceAttr.length, cd.length); - for(var i = 0; i < imax; i++) cd[i][cdAttr] = traceAttr[i]; + for(var i = 0; i < imax; i++) { + var v = traceAttr[i]; + cd[i][cdAttr] = hasFn ? fn(v) : v; + } } }; +// cast numbers to positive numbers, returns 0 if not greater than 0 +lib.mergeArrayCastPositive = function(traceAttr, cd, cdAttr) { + return lib.mergeArray(traceAttr, cd, cdAttr, function(v) { + var w = +v; + return w > 0 ? w : 0; + }); +}; + /** fills calcdata field (given by cdAttr) with traceAttr values * or function of traceAttr values (e.g. some fallback) * diff --git a/src/traces/bar/arrays_to_calcdata.js b/src/traces/bar/arrays_to_calcdata.js index a722f3dec04..4316dac2d38 100644 --- a/src/traces/bar/arrays_to_calcdata.js +++ b/src/traces/bar/arrays_to_calcdata.js @@ -8,24 +8,24 @@ 'use strict'; -var mergeArray = require('../../lib').mergeArray; +var Lib = require('../../lib'); // arrayOk attributes, merge them into calcdata array module.exports = function arraysToCalcdata(cd, trace) { for(var i = 0; i < cd.length; i++) cd[i].i = i; - mergeArray(trace.text, cd, 'tx'); - mergeArray(trace.hovertext, cd, 'htx'); + Lib.mergeArray(trace.text, cd, 'tx'); + Lib.mergeArray(trace.hovertext, cd, 'htx'); var marker = trace.marker; if(marker) { - mergeArray(marker.opacity, cd, 'mo'); - mergeArray(marker.color, cd, 'mc'); + Lib.mergeArray(marker.opacity, cd, 'mo', true); + Lib.mergeArray(marker.color, cd, 'mc'); var markerLine = marker.line; if(markerLine) { - mergeArray(markerLine.color, cd, 'mlc'); - mergeArray(markerLine.width, cd, 'mlw'); + Lib.mergeArray(markerLine.color, cd, 'mlc'); + Lib.mergeArrayCastPositive(markerLine.width, cd, 'mlw'); } } }; diff --git a/src/traces/bar/helpers.js b/src/traces/bar/helpers.js index afe5a2d6ff0..aa368a0f939 100644 --- a/src/traces/bar/helpers.js +++ b/src/traces/bar/helpers.js @@ -10,6 +10,7 @@ var isNumeric = require('fast-isnumeric'); var tinycolor = require('tinycolor2'); +var isArrayOrTypedArray = require('../../lib').isArrayOrTypedArray; exports.coerceString = function(attributeDefinition, value, defaultValue) { if(typeof value === 'string') { @@ -64,3 +65,12 @@ exports.getValue = function(arrayOrScalar, index) { else if(index < arrayOrScalar.length) value = arrayOrScalar[index]; return value; }; + +exports.getLineWidth = function(trace, di) { + var w = + (0 < di.mlw) ? di.mlw : + !isArrayOrTypedArray(trace.marker.line.width) ? trace.marker.line.width : + 0; + + return w; +}; diff --git a/src/traces/bar/hover.js b/src/traces/bar/hover.js index 68d5fb818d7..61832ac48e6 100644 --- a/src/traces/bar/hover.js +++ b/src/traces/bar/hover.js @@ -14,6 +14,7 @@ var Registry = require('../../registry'); var Color = require('../../components/color'); var fillText = require('../../lib').fillText; +var getLineWidth = require('./helpers').getLineWidth; function hoverPoints(pointData, xval, yval, hovermode) { var barPointData = hoverOnBars(pointData, xval, yval, hovermode); @@ -165,7 +166,7 @@ function hoverOnBars(pointData, xval, yval, hovermode) { function getTraceColor(trace, di) { var mc = di.mcc || trace.marker.color; var mlc = di.mlcc || trace.marker.line.color; - var mlw = di.mlw || trace.marker.line.width; + var mlw = getLineWidth(trace, di); if(Color.opacity(mc)) return mc; else if(Color.opacity(mlc) && mlw) return mlc; diff --git a/src/traces/bar/plot.js b/src/traces/bar/plot.js index 90c5fb2e46d..a52fdd7ae43 100644 --- a/src/traces/bar/plot.js +++ b/src/traces/bar/plot.js @@ -69,7 +69,7 @@ function plot(gd, plotinfo, cdModule, traceLayer, opts) { var isWaterfall = (trace.type === 'waterfall'); var isFunnel = (trace.type === 'funnel'); var isBar = (trace.type === 'bar'); - var shouldDisplayZeros = isBar || isFunnel; + var shouldDisplayZeros = (isBar || isFunnel); var adjustPixel = 0; if(isWaterfall && trace.connector.visible && trace.connector.mode === 'between') { @@ -102,12 +102,19 @@ function plot(gd, plotinfo, cdModule, traceLayer, opts) { var y0 = xy[1][0]; var y1 = xy[1][1]; - var isBlank = di.isBlank = !( - isNumeric(x0) && isNumeric(x1) && - isNumeric(y0) && isNumeric(y1) && - (x0 !== x1 || (shouldDisplayZeros && isHorizontal)) && - (y0 !== y1 || (shouldDisplayZeros && !isHorizontal)) + var isBlank = ( + x0 === x1 || + y0 === y1 || + !isNumeric(x0) || + !isNumeric(x1) || + !isNumeric(y0) || + !isNumeric(y1) ); + // display zeros if line.width > 0 + if(isBlank && shouldDisplayZeros && helpers.getLineWidth(trace, di) && (isHorizontal ? x1 - x0 === 0 : y1 - y0 === 0)) { + isBlank = false; + } + di.isBlank = isBlank; // in waterfall mode `between` we need to adjust bar end points to match the connector width if(adjustPixel) { @@ -130,8 +137,7 @@ function plot(gd, plotinfo, cdModule, traceLayer, opts) { mc = cont.color; } } else { - lw = (di.mlw + 1 || trace.marker.line.width + 1 || - (di.trace ? di.trace.marker.line.width : 0) + 1) - 1; + lw = helpers.getLineWidth(trace, di); mc = di.mc || trace.marker.color; } diff --git a/test/image/baselines/bar_display_height_zero_no_line_width.png b/test/image/baselines/bar_display_height_zero_no_line_width.png new file mode 100644 index 00000000000..ebe9e0f2e6e Binary files /dev/null and b/test/image/baselines/bar_display_height_zero_no_line_width.png differ diff --git a/test/image/baselines/bar_display_height_zero_only_line_width.png b/test/image/baselines/bar_display_height_zero_only_line_width.png new file mode 100644 index 00000000000..a71e9d0c7ce Binary files /dev/null and b/test/image/baselines/bar_display_height_zero_only_line_width.png differ diff --git a/test/image/mocks/bar_display_height_zero_no_line_width.json b/test/image/mocks/bar_display_height_zero_no_line_width.json new file mode 100644 index 00000000000..37e0338cdeb --- /dev/null +++ b/test/image/mocks/bar_display_height_zero_no_line_width.json @@ -0,0 +1,329 @@ +{ + "data": [ + { + "type": "bar", + "x": [ + "A", + "b", + "c", + "d", + "E" + ], + "y": [ + null, + 0.25, + 0.5, + 0.75, + 1 + ], + "text": [ + null, + 0.25, + 0.5, + 0.75, + 1 + ], + "textposition": "auto", + "insidetextanchor": "middle", + "cliponaxis": false + }, + { + "type": "bar", + "marker": { + "line": { + "width": "1e1" + } + }, + "x": [ + "A", + "b", + "c", + "d", + "E" + ], + "y": [ + 0, + 0, + 0.5, + null, + 1 + ], + "text": [ + 0, + 0, + 0.5, + null, + 1 + ], + "textposition": "auto", + "insidetextanchor": "middle", + "cliponaxis": false + }, + { + "type": "bar", + "x": [ + "A", + "b", + "c", + "d", + "E" + ], + "y": [ + null, + 0.25, + 0.5, + 0.75, + 1 + ], + "text": [ + null, + 0.25, + 0.5, + 0.75, + 1 + ], + "textposition": "auto", + "insidetextanchor": "middle", + "cliponaxis": false, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "bar", + "marker": { + "line": { + "width": "1e1" + } + }, + "x": [ + "A", + "b", + "c", + "d", + "E" + ], + "y": [ + 0, + 0, + 0.5, + null, + 1 + ], + "text": [ + 0, + 0, + 0.5, + null, + 1 + ], + "textposition": "auto", + "insidetextanchor": "middle", + "cliponaxis": false, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "bar", + "orientation": "h", + "y": [ + "A", + "b", + "c", + "d", + "E" + ], + "x": [ + null, + 0.25, + 0.5, + 0.75, + 1 + ], + "text": [ + null, + 0.25, + 0.5, + 0.75, + 1 + ], + "textposition": "auto", + "insidetextanchor": "middle", + "cliponaxis": false, + "xaxis": "x3", + "yaxis": "y3" + }, + { + "type": "bar", + "marker": { + "line": { + "width": "1e1" + } + }, + "orientation": "h", + "y": [ + "A", + "b", + "c", + "d", + "E" + ], + "x": [ + 0, + 0, + 0.5, + null, + 1 + ], + "text": [ + 0, + 0, + 0.5, + null, + 1 + ], + "textposition": "auto", + "insidetextanchor": "middle", + "cliponaxis": false, + "xaxis": "x3", + "yaxis": "y3" + }, + { + "type": "bar", + "orientation": "h", + "y": [ + "A", + "b", + "c", + "d", + "E" + ], + "x": [ + null, + 0.25, + 0.5, + 0.75, + 1 + ], + "text": [ + null, + 0.25, + 0.5, + 0.75, + 1 + ], + "textposition": "auto", + "insidetextanchor": "middle", + "cliponaxis": false, + "xaxis": "x4", + "yaxis": "y4" + }, + { + "type": "bar", + "marker": { + "line": { + "width": "1e1" + } + }, + "orientation": "h", + "y": [ + "A", + "b", + "c", + "d", + "E" + ], + "x": [ + 0, + 0, + 0.5, + null, + 1 + ], + "text": [ + 0, + 0, + 0.5, + null, + 1 + ], + "textposition": "auto", + "insidetextanchor": "middle", + "cliponaxis": false, + "xaxis": "x4", + "yaxis": "y4" + } + ], + "layout": { + "showlegend": true, + "width": 800, + "height": 800, + "dragmode": "pan", + "barmode": "relative", + "xaxis": { + "zeroline": false, + "domain": [ + 0, + 0.48 + ] + }, + "xaxis2": { + "zeroline": false, + "autorange": "reversed", + "anchor": "y2", + "domain": [ + 0.52, + 1 + ] + }, + "xaxis3": { + "zeroline": false, + "anchor": "y3", + "domain": [ + 0, + 0.48 + ] + }, + "xaxis4": { + "zeroline": false, + "autorange": "reversed", + "anchor": "y4", + "domain": [ + 0.52, + 1 + ] + }, + "yaxis": { + "zeroline": false, + "domain": [ + 0, + 0.48 + ] + }, + "yaxis2": { + "zeroline": false, + "autorange": "reversed", + "anchor": "x2", + "domain": [ + 0.52, + 1 + ] + }, + "yaxis3": { + "zeroline": false, + "anchor": "x3", + "domain": [ + 0.52, + 1 + ] + }, + "yaxis4": { + "zeroline": false, + "autorange": "reversed", + "anchor": "x4", + "domain": [ + 0, + 0.48 + ] + } + } +} diff --git a/test/image/mocks/bar_display_height_zero_only_line_width.json b/test/image/mocks/bar_display_height_zero_only_line_width.json new file mode 100644 index 00000000000..b4e8983bd88 --- /dev/null +++ b/test/image/mocks/bar_display_height_zero_only_line_width.json @@ -0,0 +1,348 @@ +{ + "data": [ + { + "type": "bar", + "marker": { + "line": { + "width": 0 + } + }, + "x": [ + "A", + "b", + "c", + "d", + "E" + ], + "y": [ + 0, + 0, + null, + null, + 1 + ], + "text": [ + 0, + 0, + null, + null, + 1 + ], + "textposition": "auto", + "insidetextanchor": "middle", + "cliponaxis": false + }, + { + "type": "bar", + "marker": { + "line": { + "width": ["1e1", "-5", "1e1", "0.0", "1e1"] + } + }, + "x": [ + "A", + "b", + "c", + "d", + "E" + ], + "y": [ + 0, + 0, + null, + null, + 1 + ], + "text": [ + 0, + 0, + null, + null, + 1 + ], + "textposition": "auto", + "insidetextanchor": "middle", + "cliponaxis": false + }, + { + "type": "bar", + "marker": { + "line": { + "width": 0 + } + }, + "x": [ + "A", + "b", + "c", + "d", + "E" + ], + "y": [ + 0, + 0, + null, + null, + 1 + ], + "text": [ + 0, + 0, + null, + null, + 1 + ], + "textposition": "auto", + "insidetextanchor": "middle", + "cliponaxis": false, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "bar", + "marker": { + "line": { + "width": ["1e1", "-5", "1e1", "0.0", "1e1"] + } + }, + "x": [ + "A", + "b", + "c", + "d", + "E" + ], + "y": [ + 0, + 0, + null, + null, + 1 + ], + "text": [ + 0, + 0, + null, + null, + 1 + ], + "textposition": "auto", + "insidetextanchor": "middle", + "cliponaxis": false, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "bar", + "marker": { + "line": { + "width": 0 + } + }, + "orientation": "h", + "y": [ + "A", + "b", + "c", + "d", + "E" + ], + "x": [ + 0, + 0, + null, + null, + 1 + ], + "text": [ + 0, + 0, + null, + null, + 1 + ], + "textposition": "auto", + "insidetextanchor": "middle", + "cliponaxis": false, + "xaxis": "x3", + "yaxis": "y3" + }, + { + "type": "bar", + "marker": { + "line": { + "width": ["1e1", "-5", "1e1", "0.0", "1e1"] + } + }, + "orientation": "h", + "y": [ + "A", + "b", + "c", + "d", + "E" + ], + "x": [ + 0, + 0, + null, + null, + 1 + ], + "text": [ + 0, + 0, + null, + null, + 1 + ], + "textposition": "auto", + "insidetextanchor": "middle", + "cliponaxis": false, + "xaxis": "x3", + "yaxis": "y3" + }, + { + "type": "bar", + "marker": { + "line": { + "width": 0 + } + }, + "orientation": "h", + "y": [ + "A", + "b", + "c", + "d", + "E" + ], + "x": [ + 0, + 0, + null, + null, + 1 + ], + "text": [ + 0, + 0, + null, + null, + 1 + ], + "textposition": "auto", + "insidetextanchor": "middle", + "cliponaxis": false, + "xaxis": "x4", + "yaxis": "y4" + }, + { + "type": "bar", + "marker": { + "line": { + "width": ["1e1", "-5", "1e1", "0.0", "1e1"] + } + }, + "orientation": "h", + "y": [ + "A", + "b", + "c", + "d", + "E" + ], + "x": [ + 0, + 0, + null, + null, + 1 + ], + "text": [ + 0, + 0, + null, + null, + 1 + ], + "textposition": "auto", + "insidetextanchor": "middle", + "cliponaxis": false, + "xaxis": "x4", + "yaxis": "y4" + } + ], + "layout": { + "showlegend": true, + "width": 800, + "height": 800, + "dragmode": "pan", + "xaxis": { + "zeroline": false, + "domain": [ + 0, + 0.48 + ] + }, + "xaxis2": { + "zeroline": false, + "autorange": "reversed", + "anchor": "y2", + "domain": [ + 0.52, + 1 + ] + }, + "xaxis3": { + "zeroline": false, + "anchor": "y3", + "domain": [ + 0, + 0.48 + ] + }, + "xaxis4": { + "zeroline": false, + "autorange": "reversed", + "anchor": "y4", + "domain": [ + 0.52, + 1 + ] + }, + "yaxis": { + "zeroline": false, + "domain": [ + 0, + 0.48 + ] + }, + "yaxis2": { + "zeroline": false, + "autorange": "reversed", + "anchor": "x2", + "domain": [ + 0.52, + 1 + ] + }, + "yaxis3": { + "zeroline": false, + "anchor": "x3", + "domain": [ + 0.52, + 1 + ] + }, + "yaxis4": { + "zeroline": false, + "autorange": "reversed", + "anchor": "x4", + "domain": [ + 0, + 0.48 + ] + } + } +}