diff --git a/src/components/colorbar/defaults.js b/src/components/colorbar/defaults.js index 117e8457272..dba84a962e4 100644 --- a/src/components/colorbar/defaults.js +++ b/src/components/colorbar/defaults.js @@ -13,6 +13,7 @@ var Lib = require('../../lib'); var handleTickValueDefaults = require('../../plots/cartesian/tick_value_defaults'); var handleTickMarkDefaults = require('../../plots/cartesian/tick_mark_defaults'); var handleTickLabelDefaults = require('../../plots/cartesian/tick_label_defaults'); +var cartesianAxesAttrs = require('../../plots/cartesian/layout_attributes'); var attributes = require('./attributes'); @@ -21,8 +22,11 @@ module.exports = function colorbarDefaults(containerIn, containerOut, layout) { var colorbarOut = containerOut.colorbar = {}, colorbarIn = containerIn.colorbar || {}; + //colorbar doesn't currently implement tickpadding property, but it is a required attribute of a cartesian axis + var cartesianAttributes = Lib.extendFlat({}, attributes, { tickpadding: cartesianAxesAttrs.tickpadding }); + function coerce(attr, dflt) { - return Lib.coerce(colorbarIn, colorbarOut, attributes, attr, dflt); + return Lib.coerce(colorbarIn, colorbarOut, cartesianAttributes, attr, dflt); } var thicknessmode = coerce('thicknessmode'); diff --git a/src/components/colorbar/draw.js b/src/components/colorbar/draw.js index e8a91d78ffc..4d25ccfb968 100644 --- a/src/components/colorbar/draw.js +++ b/src/components/colorbar/draw.js @@ -156,6 +156,7 @@ module.exports = function draw(gd, id) { showticklabels: opts.showticklabels, tickfont: opts.tickfont, tickangle: opts.tickangle, + tickpadding: opts.tickpadding, tickformat: opts.tickformat, exponentformat: opts.exponentformat, separatethousands: opts.separatethousands, diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js index 8e574fe49e9..f9b5307c73d 100644 --- a/src/plots/cartesian/axes.js +++ b/src/plots/cartesian/axes.js @@ -535,10 +535,13 @@ axes.calcTicks = function calcTicks(ax) { if(!nt) { if(ax.type === 'category') { minPx = ax.tickfont ? (ax.tickfont.size || 12) * 1.2 : 15; + minPx += ax.tickpadding; nt = ax._length / minPx; } else { minPx = ax._id.charAt(0) === 'y' ? 40 : 80; + minPx += ax.tickpadding; + nt = Lib.constrain(ax._length / minPx, 4, 9) + 1; } } diff --git a/src/plots/cartesian/layout_attributes.js b/src/plots/cartesian/layout_attributes.js index a0e44874bd6..0fe047b249d 100644 --- a/src/plots/cartesian/layout_attributes.js +++ b/src/plots/cartesian/layout_attributes.js @@ -251,6 +251,15 @@ module.exports = { 'vertically.' ].join(' ') }, + tickpadding: { + valType: 'number', + dflt: 0, + role: 'style', + description: [ + 'Sets extra padding between ticks.', + 'This can be used to control the number of ticks displayed without explicitly specifying nticks.' + ].join(' ') + }, tickprefix: { valType: 'string', dflt: '', diff --git a/src/plots/cartesian/tick_value_defaults.js b/src/plots/cartesian/tick_value_defaults.js index b51994b5ce7..33f34f3f9a1 100644 --- a/src/plots/cartesian/tick_value_defaults.js +++ b/src/plots/cartesian/tick_value_defaults.js @@ -10,7 +10,7 @@ 'use strict'; var isNumeric = require('fast-isnumeric'); - +var layoutAttributes = require('./layout_attributes'); module.exports = function handleTickValueDefaults(containerIn, containerOut, coerce, axType) { var tickmodeDefault = 'auto'; @@ -26,7 +26,17 @@ module.exports = function handleTickValueDefaults(containerIn, containerOut, coe } var tickmode = coerce('tickmode', tickmodeDefault); - if(tickmode === 'auto') coerce('nticks'); + if(tickmode === 'auto') { + var nticks = coerce('nticks'); + + //Only use tickpadding if tickmode is 'auto' and the user doesn't specify nticks + if(nticks === layoutAttributes.nticks.dflt) { + coerce('tickpadding'); + } + else { + containerOut.tickpadding = layoutAttributes.tickpadding.dflt; + } + } else if(tickmode === 'linear') { coerce('tick0'); coerce('dtick'); diff --git a/src/plots/gl3d/layout/axis_defaults.js b/src/plots/gl3d/layout/axis_defaults.js index 8332efa8bd9..1fc5f240de3 100644 --- a/src/plots/gl3d/layout/axis_defaults.js +++ b/src/plots/gl3d/layout/axis_defaults.js @@ -15,6 +15,7 @@ var Lib = require('../../../lib'); var layoutAttributes = require('./axis_attributes'); var handleAxisDefaults = require('../../cartesian/axis_defaults'); +var cartesianAxesAttrs = require('../../cartesian/layout_attributes'); var axesNames = ['xaxis', 'yaxis', 'zaxis']; var noop = function() {}; @@ -26,8 +27,11 @@ var gridLightness = 100 * (204 - 0x44) / (255 - 0x44); module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, options) { var containerIn, containerOut; + //gl3d doesn't currently implement tickpadding property, but it is a required attribute of a cartesian axis + var cartesianLayoutAttributes = Lib.extendFlat({}, layoutAttributes, { tickpadding: cartesianAxesAttrs.tickpadding }); + function coerce(attr, dflt) { - return Lib.coerce(containerIn, containerOut, layoutAttributes, attr, dflt); + return Lib.coerce(containerIn, containerOut, cartesianLayoutAttributes, attr, dflt); } for(var j = 0; j < axesNames.length; j++) { diff --git a/test/image/baselines/axes-tickpadding.png b/test/image/baselines/axes-tickpadding.png new file mode 100644 index 00000000000..9b3f877e5ce Binary files /dev/null and b/test/image/baselines/axes-tickpadding.png differ diff --git a/test/image/mocks/axes-tickpadding.json b/test/image/mocks/axes-tickpadding.json new file mode 100644 index 00000000000..52e33be6ad7 --- /dev/null +++ b/test/image/mocks/axes-tickpadding.json @@ -0,0 +1,103 @@ +{ + "data": [ + { + "x": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39 + ], + "y": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39 + ], + "type": "scatter" + } + ], + "layout": { + "xaxis": { + "type": "category", + "tickmode": "auto", + "tickpadding": 15 + }, + "yaxis": { + "type": "category", + "tickmode": "auto", + "tickpadding": 20 + } + } +} \ No newline at end of file diff --git a/test/jasmine/tests/axes_test.js b/test/jasmine/tests/axes_test.js index 619a22098cf..b769d2507c9 100644 --- a/test/jasmine/tests/axes_test.js +++ b/test/jasmine/tests/axes_test.js @@ -556,6 +556,7 @@ describe('Test axes', function() { expect(yaxis.showticklabels).toBe(true); expect(yaxis.tickfont).toEqual({ family: '"Open Sans", verdana, arial, sans-serif', size: 12, color: '#444' }); expect(yaxis.tickangle).toBe('auto'); + expect(yaxis.tickpadding).toBe(0); }); it('should use valid inputs', function() { @@ -566,7 +567,9 @@ describe('Test axes', function() { tickcolor: '#F00', showticklabels: true, tickfont: { family: 'Garamond', size: 72, color: '#0FF' }, - tickangle: -20 + tickangle: -20, + tickmode: 'auto', + tickpadding: 15 } }; @@ -580,6 +583,7 @@ describe('Test axes', function() { expect(yaxis.showticklabels).toBe(true); expect(yaxis.tickfont).toEqual({ family: 'Garamond', size: 72, color: '#0FF' }); expect(yaxis.tickangle).toBe(-20); + expect(yaxis.tickpadding).toBe(15); }); it('should conditionally coerce based on showticklabels', function() { @@ -595,6 +599,21 @@ describe('Test axes', function() { var yaxis = gd._fullLayout.yaxis; expect(yaxis.tickangle).toBeUndefined(); }); + + it('should conditionally coerce tickpadding if nticks is set', function() { + var layout = { + yaxis: { + tickmode: 'auto', + nticks: 5, + tickpadding: 20 + } + }; + + PlotlyInternal.plot(gd, data, layout); + + var yaxis = gd._fullLayout.yaxis; + expect(yaxis.tickpadding).toBe(0); + }); }); describe('handleTickValueDefaults', function() {