diff --git a/src/traces/bar/plot.js b/src/traces/bar/plot.js index 78728914a92..aed04eb5dc4 100644 --- a/src/traces/bar/plot.js +++ b/src/traces/bar/plot.js @@ -147,6 +147,7 @@ function plot(gd, plotinfo, cdModule, traceLayer, opts, makeOnCompleteCallback) !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; @@ -156,6 +157,9 @@ function plot(gd, plotinfo, cdModule, traceLayer, opts, makeOnCompleteCallback) if(isBlank && isHorizontal) x1 = x0; if(isBlank && !isHorizontal) y1 = y0; + var spansHorizontal = isHorizontal && (x0 !== x1); + var spansVertical = !isHorizontal && (y0 !== y1); + // in waterfall mode `between` we need to adjust bar end points to match the connector width if(adjustPixel && !isBlank) { if(isHorizontal) { @@ -210,10 +214,15 @@ function plot(gd, plotinfo, cdModule, traceLayer, opts, makeOnCompleteCallback) var op = Color.opacity(mc); var fixpx = (op < 1 || lw > 0.01) ? roundWithLine : expandToVisible; - x0 = fixpx(x0, x1); - x1 = fixpx(x1, x0); - y0 = fixpx(y0, y1); - y1 = fixpx(y1, y0); + + if(spansHorizontal) { + x0 = fixpx(x0, x1); + x1 = fixpx(x1, x0); + } + if(spansVertical) { + y0 = fixpx(y0, y1); + y1 = fixpx(y1, y0); + } } var sel = transition(Lib.ensureSingle(bar, 'path'), fullLayout, opts, makeOnCompleteCallback); diff --git a/test/image/baselines/bar_hide_nulls.png b/test/image/baselines/bar_hide_nulls.png new file mode 100644 index 00000000000..6ada4aa9928 Binary files /dev/null and b/test/image/baselines/bar_hide_nulls.png differ diff --git a/test/image/mocks/bar_hide_nulls.json b/test/image/mocks/bar_hide_nulls.json new file mode 100644 index 00000000000..3f68cf7c615 --- /dev/null +++ b/test/image/mocks/bar_hide_nulls.json @@ -0,0 +1,192 @@ +{ + "data": [ + { + "type": "bar", + "x": [ + "A", + "B", + "C", + "D" + ], + "y": [ + 0, + null, + 0.001, + 1 + ], + "text": [ + 0, + null, + 0.001, + 1 + ], + "textposition": "auto", + "insidetextanchor": "middle", + "cliponaxis": false + }, + { + "type": "bar", + "x": [ + "A", + "B", + "C", + "D" + ], + "y": [ + 0, + null, + 0.001, + 1 + ], + "text": [ + 0, + null, + 0.001, + 1 + ], + "textposition": "auto", + "insidetextanchor": "middle", + "cliponaxis": false, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "bar", + "orientation": "h", + "x": [ + 0, + null, + 0.001, + 1 + ], + "y": [ + "A", + "B", + "C", + "D" + ], + "text": [ + 0, + null, + 0.001, + 1 + ], + "textposition": "auto", + "insidetextanchor": "middle", + "cliponaxis": false, + "xaxis": "x3", + "yaxis": "y3" + }, + { + "type": "bar", + "orientation": "h", + "x": [ + 0, + null, + 0.001, + 1 + ], + "y": [ + "A", + "B", + "C", + "D" + ], + "text": [ + 0, + null, + 0.001, + 1 + ], + "textposition": "auto", + "insidetextanchor": "middle", + "cliponaxis": false, + "xaxis": "x4", + "yaxis": "y4" + } + ], + "layout": { + "showlegend": false, + "width": 800, + "height": 800, + "dragmode": "pan", + "xaxis": { + "domain": [ + 0, + 0.48 + ] + }, + "xaxis2": { + "autorange": "reversed", + "anchor": "y2", + "domain": [ + 0.52, + 1 + ] + }, + "xaxis3": { + "range": [ + -0.01, + 1 + ], + "zeroline": false, + "anchor": "y3", + "domain": [ + 0, + 0.48 + ] + }, + "xaxis4": { + "range": [ + -0.01, + 1 + ], + "zeroline": false, + "autorange": "reversed", + "anchor": "y4", + "domain": [ + 0.52, + 1 + ] + }, + "yaxis": { + "range": [ + -0.01, + 1 + ], + "zeroline": false, + "domain": [ + 0, + 0.48 + ] + }, + "yaxis2": { + "range": [ + -0.01, + 1 + ], + "zeroline": false, + "autorange": "reversed", + "anchor": "x2", + "domain": [ + 0.52, + 1 + ] + }, + "yaxis3": { + "anchor": "x3", + "domain": [ + 0.52, + 1 + ] + }, + "yaxis4": { + "autorange": "reversed", + "anchor": "x4", + "domain": [ + 0, + 0.48 + ] + } + } +} diff --git a/test/jasmine/tests/bar_test.js b/test/jasmine/tests/bar_test.js index 4057ef63713..171d7b8e3c6 100644 --- a/test/jasmine/tests/bar_test.js +++ b/test/jasmine/tests/bar_test.js @@ -2025,6 +2025,57 @@ describe('A bar plot', function() { .catch(failTest) .then(done); }); + + it('should not show up null and zero bars as thin bars', function(done) { + var mock = Lib.extendDeep({}, require('@mocks/bar_hide_nulls.json')); + + function getArea(path) { + var pos = path + .substr(1, path.length - 2) + .replace('V', ',') + .replace('H', ',') + .replace('V', ',') + .split(','); + var dx = +pos[0]; + var dy = +pos[1]; + dy -= +pos[2]; + dx -= +pos[3]; + + return Math.abs(dx * dy); + } + + Plotly.plot(gd, mock) + .then(function() { + var nodes = gd.querySelectorAll('g.point > path'); + expect(nodes.length).toBe(16, '# of bars'); + + [ + [0, false], + [1, false], + [2, true], + [3, true], + [4, false], + [5, false], + [6, true], + [7, true], + [8, false], + [9, false], + [10, true], + [11, true], + [12, false], + [13, false], + [14, true], + [15, true] + ].forEach(function(e) { + var i = e[0]; + var d = nodes[i].getAttribute('d'); + var visible = e[1]; + expect(getArea(d) > 0).toBe(visible, 'item:' + i); + }); + }) + .catch(failTest) + .then(done); + }); }); describe('bar visibility toggling:', function() {