diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js index 9adbe25fb55..c0a94522ab4 100644 --- a/src/plots/cartesian/axes.js +++ b/src/plots/cartesian/axes.js @@ -1584,10 +1584,8 @@ axes.draw = function(gd, arg, opts) { plotinfo.xaxislayer.selectAll('.' + xa._id + 'tick').remove(); plotinfo.yaxislayer.selectAll('.' + ya._id + 'tick').remove(); - if(xa.type === 'multicategory') { - plotinfo.xaxislayer.selectAll('.' + xa._id + 'tick2').remove(); - plotinfo.xaxislayer.selectAll('.' + xa._id + 'divider').remove(); - } + plotinfo.xaxislayer.selectAll('.' + xa._id + 'tick2').remove(); + plotinfo.xaxislayer.selectAll('.' + xa._id + 'divider').remove(); if(plotinfo.gridlayer) plotinfo.gridlayer.selectAll('path').remove(); if(plotinfo.zerolinelayer) plotinfo.zerolinelayer.selectAll('path').remove(); fullLayout._infolayer.select('.g-' + xa._id + 'title').remove(); @@ -1781,11 +1779,12 @@ axes.drawOne = function(gd, ax, opts) { if(ax.type === 'multicategory') { var labelLength = 0; var pad = {x: 2, y: 10}[axLetter]; + var sgn = tickSigns[2] * (ax.ticks === 'inside' ? -1 : 1); seq.push(function() { labelLength += getLabelLevelSpan(ax, axId + 'tick') + pad; labelLength += ax._tickAngles[axId + 'tick'] ? ax.tickfont.size * LINE_SPACING : 0; - var secondaryPosition = mainLinePosition + labelLength * tickSigns[2]; + var secondaryPosition = mainLinePosition + labelLength * sgn; var secondaryLabelFns = axes.makeLabelFns(ax, secondaryPosition); return axes.drawLabels(gd, ax, { @@ -1803,11 +1802,12 @@ axes.drawOne = function(gd, ax, opts) { seq.push(function() { labelLength += getLabelLevelSpan(ax, axId + 'tick2'); + ax._labelLength = labelLength; return drawDividers(gd, ax, { vals: dividerVals, layer: mainAxLayer, - path: axes.makeTickPath(ax, mainLinePosition, tickSigns[2], labelLength), + path: axes.makeTickPath(ax, mainLinePosition, sgn, labelLength), transFn: transFn }); }); @@ -2608,7 +2608,7 @@ function drawTitle(gd, ax) { var titleStandoff; if(ax.type === 'multicategory') { - titleStandoff = ax._boundingBox[{x: 'height', y: 'width'}[axLetter]]; + titleStandoff = ax._labelLength; } else { var offsetBase = 1.5; titleStandoff = 10 + fontSize * offsetBase + (ax.linewidth ? ax.linewidth - 1 : 0); diff --git a/test/image/baselines/multicategory-inside-ticks.png b/test/image/baselines/multicategory-inside-ticks.png new file mode 100644 index 00000000000..c01f1b712ef Binary files /dev/null and b/test/image/baselines/multicategory-inside-ticks.png differ diff --git a/test/image/baselines/multicategory-mirror.png b/test/image/baselines/multicategory-mirror.png index 88a03453a83..c56a0dcd6ba 100644 Binary files a/test/image/baselines/multicategory-mirror.png and b/test/image/baselines/multicategory-mirror.png differ diff --git a/test/image/baselines/multicategory.png b/test/image/baselines/multicategory.png index ce31358c8d7..22962eb669f 100644 Binary files a/test/image/baselines/multicategory.png and b/test/image/baselines/multicategory.png differ diff --git a/test/image/mocks/multicategory-inside-ticks.json b/test/image/mocks/multicategory-inside-ticks.json new file mode 100644 index 00000000000..82d15c4fa77 --- /dev/null +++ b/test/image/mocks/multicategory-inside-ticks.json @@ -0,0 +1,76 @@ +{ + "data": [ + { + "type": "bar", + "x": [ + ["2017", "2017", "2017", "2017", "2018", "2018", "2018"], + ["q1", "q2", "q3", "q4", "q1", "q2", "q3" ] + ], + "y": [1, 2, 3, 1, 3, 2, 3, 1] + }, + { + "type": "bar", + "x": [ + ["2017", "2017", "2017", "2017", "2018", "2018", "2018"], + ["q1", "q2", "q3", "q4", "q1", "q2", "q3"] + ], + "y": [1.12, 2.15, 3.07, 1.48, 2.78, 1.95, 2.54, 0.64] + }, + + { + "type": "bar", + "x": [ + ["2017", "2017", "2017", "2017", "2018", "2018", "2018"], + ["q1", "q2", "q3", "q4", "q1", "q2", "q3"] + ], + "y": [1.12, 2.15, 3.07, 1.48, 2.78, 1.95, 2.54, 0.64], + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "bar", + "x": [ + ["2017", "2017", "2017", "2017", "2018", "2018", "2018"], + ["q1", "q2", "q3", "q4", "q1", "q2", "q3" ] + ], + "y": [1, 2, 3, 1, 3, 2, 3, 1], + "xaxis": "x2", + "yaxis": "y2" + } + ], + "layout": { + "grid": {"rows": 2, "columns": 1, "pattern": "independent"}, + "xaxis": { + "side": "top", + "title": "MULTI-CATEGORY", + "range": [-0.5, 7], + "showline": true, + "tickfont": {"size": 16}, + "ticks": "inside", + "ticklen": 10, + "tickcolor": "red", + "tickwidth": 2, + "dividercolor": "blue", + "dividerwidth": 2 + }, + "xaxis2": { + "title": "MULTI-CATEGORY", + "range": [-1, 6.5], + "showline": true, + "tickfont": {"size": 16}, + "ticks": "inside", + "ticklen": 10, + "tickcolor": "red", + "tickwidth": 2, + "dividercolor": "blue", + "dividerwidth": 2 + }, + "yaxis": { + "zeroline": false + }, + "yaxis2": { + "zeroline": false + }, + "showlegend": false + } +} diff --git a/test/image/mocks/multicategory-mirror.json b/test/image/mocks/multicategory-mirror.json index dd11116228e..8142833caa2 100644 --- a/test/image/mocks/multicategory-mirror.json +++ b/test/image/mocks/multicategory-mirror.json @@ -19,6 +19,7 @@ ], "layout": { "xaxis": { + "title": "MULTI-CATEGORY", "ticks": "outside", "showline": true, "mirror": "ticks", diff --git a/test/jasmine/tests/cartesian_test.js b/test/jasmine/tests/cartesian_test.js index e4d9888e2cf..aa816bcb1d8 100644 --- a/test/jasmine/tests/cartesian_test.js +++ b/test/jasmine/tests/cartesian_test.js @@ -849,4 +849,62 @@ describe('subplot creation / deletion:', function() { .catch(failTest) .then(done); }); + + it('clears secondary labels and divider when updating out of axis type multicategory', function(done) { + function _assert(msg, exp) { + var gd3 = d3.select(gd); + expect(gd3.selectAll('.xtick > text').size()) + .toBe(exp.tickCnt, msg + ' # labels'); + expect(gd3.selectAll('.xtick2 > text').size()) + .toBe(exp.tick2Cnt, msg + ' # secondary labels'); + expect(gd3.selectAll('.xdivider').size()) + .toBe(exp.dividerCnt, msg + ' # dividers'); + } + + Plotly.react(gd, [{ + type: 'bar', + x: ['a', 'b', 'c'], + y: [1, 2, 1] + }]) + .then(function() { + _assert('base - category axis', { + tickCnt: 3, + tick2Cnt: 0, + dividerCnt: 0 + }); + }) + .then(function() { + return Plotly.react(gd, [{ + type: 'bar', + x: [ + ['d', 'd', 'e'], + ['a', 'b', 'c'] + ], + y: [1, 2, 3] + }]); + }) + .then(function() { + _assert('multicategory axis', { + tickCnt: 3, + tick2Cnt: 2, + dividerCnt: 3 + }); + }) + .then(function() { + return Plotly.react(gd, [{ + type: 'bar', + x: ['a', 'b', 'c'], + y: [1, 2, 1] + }]); + }) + .then(function() { + _assert('back to category axis', { + tickCnt: 3, + tick2Cnt: 0, + dividerCnt: 0 + }); + }) + .catch(failTest) + .then(done); + }); });