Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add additional options for automargin property #6193

Merged
1 change: 1 addition & 0 deletions draftlogs/6193_add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Add flaglist options including "left", "right", "top", "bottom", "width" and "height" to control the direction of `automargin` on cartesian axes [[#6193](https://github.com/plotly/plotly.js/pull/6193)]
8 changes: 4 additions & 4 deletions src/lib/coerce.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,14 +215,14 @@ exports.valObjectMeta = {
requiredOpts: ['flags'],
otherOpts: ['dflt', 'extras', 'arrayOk'],
coerceFunction: function(v, propOut, dflt, opts) {
if(typeof v !== 'string') {
propOut.set(dflt);
return;
}
if((opts.extras || []).indexOf(v) !== -1) {
propOut.set(v);
return;
}
if(typeof v !== 'string') {
propOut.set(dflt);
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please elaborate why this change is needed?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just reversing the order of short-circuits to support non-string extras #6193 (comment)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As @alexcjohnson wrote:

OK that's pretty good evidence that we don't currently have any flaglists with non-string extras 😉
We could allow this in coerce pretty easily by pushing the extras test up above that non-string short-circuit.

Without this, the flaglist coerce function does not pass non-string values.

var vParts = v.split('+');
var i = 0;
while(i < vParts.length) {
Expand Down
32 changes: 32 additions & 0 deletions src/plots/cartesian/axes.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ var GRID_PATH = { K: 'gridline', L: 'path' };
var MINORGRID_PATH = { K: 'minor-gridline', L: 'path' };
var TICK_PATH = { K: 'tick', L: 'path' };
var TICK_TEXT = { K: 'tick', L: 'text' };
var MARGIN_MAPPING = {
width: ['x', 'r', 'l', 'xl', 'xr'],
height: ['y', 't', 'b', 'yt', 'yb'],
right: ['r', 'xr'],
left: ['l', 'xl'],
top: ['t', 'yt'],
bottom: ['b', 'yb']
};

var alignmentConstants = require('../../constants/alignment');
var MID_SHIFT = alignmentConstants.MID_SHIFT;
Expand Down Expand Up @@ -2622,6 +2630,11 @@ axes.drawOne = function(gd, ax, opts) {
rangeSliderPush = Registry.getComponentMethod('rangeslider', 'autoMarginOpts')(gd, ax);
}

if(typeof ax.automargin === 'string') {
filterPush(push, ax.automargin);
filterPush(mirrorPush, ax.automargin);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about rangeSliderPush? Should we filter that one too?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ha @nickmelnikov82 asked me the same question, my answer:

that one isn’t controlled by ax.automargin so I think no

There are others that aren't even in this section of code, like legends and colorbars. ax.automargin is only about "are the axes themselves allowed to increase the margins."

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we want to implement layout.automargindirections and inherit from it instead?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh that's interesting... in principle I like this, one place to control all of this rather than requiring you to set it in each axis separately. But it opens a can of worms: by default axes have automargin: false while colorbars and legends (and rangesliders) by default DO increase the margins, and putting this at the top level would imply that it applies to everything. So it would need a default value like 'legacy' or something that gets inherited differently by axes and other objects, and then we would need to extend support for disabling automargin to all these other objects as well.

All that to say: it's a good idea, but it's going to be a bunch of work, so let's not do it now. What we're doing in this PR would be fully compatible with adding that sometime in the future.

}

Plots.autoMargin(gd, axAutoMarginID(ax), push);
Plots.autoMargin(gd, axMirrorAutoMarginID(ax), mirrorPush);
Plots.autoMargin(gd, rangeSliderAutoMarginID(ax), rangeSliderPush);
Expand All @@ -2636,6 +2649,25 @@ axes.drawOne = function(gd, ax, opts) {
return Lib.syncOrAsync(seq);
};

function filterPush(push, automargin) {
if(!push) return push;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if(!push) return push;
if(!push) return;


var keepMargin = Object.keys(MARGIN_MAPPING).reduce(function(data, nextKey) {
if(automargin.indexOf(nextKey) !== -1) {
MARGIN_MAPPING[nextKey].forEach(function(key) { data[key] = 1;});
}
return data;
}, {});

Object.keys(push).forEach(function(key) {
if(!keepMargin[key]) {
if(key.length === 1) push[key] = 0;
else delete push[key];
}
});
return push;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return push;

}

function getBoundaryVals(ax, vals) {
var out = [];
var i;
Expand Down
4 changes: 3 additions & 1 deletion src/plots/cartesian/layout_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,9 @@ module.exports = {
description: 'Determines whether or not the tick labels are drawn.'
},
automargin: {
valType: 'boolean',
valType: 'flaglist',
flags: ['height', 'width', 'left', 'right', 'top', 'bottom'],
extras: [true, false],
dflt: false,
editType: 'ticks',
description: [
Expand Down
126 changes: 126 additions & 0 deletions test/jasmine/tests/axes_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4225,6 +4225,132 @@ describe('Test axes', function() {
.then(done, done.fail);
});

it('should handle partial automargin', function(done) {
var initialSize;

function assertSize(msg, actual, exp) {
for(var k in exp) {
var parts = exp[k].split('|');
var op = parts[0];

var method = {
'=': 'toBe',
grew: 'toBeGreaterThan',
}[op];

var val = initialSize[k];
var msgk = msg + ' ' + k + (parts[1] ? ' |' + parts[1] : '');
var args = op === '~=' ? [val, 1.1, msgk] : [val, msgk, ''];

expect(actual[k])[method](args[0], args[1], args[2]);
}
}

function check(msg, relayoutObj, exp) {
return function() {
return Plotly.relayout(gd, relayoutObj).then(function() {
var gs = Lib.extendDeep({}, gd._fullLayout._size);
assertSize(msg, gs, exp);
});
};
}

Plotly.newPlot(gd, [{
x: [
'short label 1', 'loooooong label 1',
'short label 2', 'loooooong label 2',
'short label 3', 'loooooong label 3',
'short label 4', 'loooooongloooooongloooooong label 4',
'short label 5', 'loooooong label 5'
],
y: [
'short label 1', 'loooooong label 1',
'short label 2', 'loooooong label 2',
'short label 3', 'loooooong label 3',
'short label 4', 'loooooong label 4',
'short label 5', 'loooooong label 5'
]
}], {
margin: {l: 0, r: 0, b: 0, t: 0},
width: 600, height: 600
})
.then(function() {
expect(gd._fullLayout.xaxis._tickAngles.xtick).toBe(30);

var gs = gd._fullLayout._size;
initialSize = Lib.extendDeep({}, gs);
})
.then(check('automargin y', {'yaxis.automargin': true, 'yaxis.tickangle': 30, 'yaxis.ticklen': 30}, {
t: 'grew', l: 'grew',
b: '=', r: '='
}))
.then(check('automargin not left', {'yaxis.automargin': 'right+height'}, {
t: 'grew', l: '=',
b: '=', r: '='
}))
.then(check('automargin keep left height', {'yaxis.automargin': 'left+height'}, {
t: 'grew', l: 'grew',
b: '=', r: '='
}))
.then(check('automargin keep bottom right', {'yaxis.automargin': 'bottom+right'}, {
t: '=', l: '=',
b: '=', r: '='
}))
.then(check('automargin keep height', {'yaxis.automargin': 'height'}, {
t: 'grew', l: '=',
b: '=', r: '='
}))
.then(check('automargin keep top', {'yaxis.automargin': 'top'}, {
t: 'grew', l: '=',
b: '=', r: '='
}))
.then(check('automargin not top', {'yaxis.automargin': 'bottom+width'}, {
t: '=', l: 'grew',
b: '=', r: '='
}))
.then(check('automargin keep left', {'yaxis.automargin': 'left'}, {
t: '=', l: 'grew',
b: '=', r: '='
}))
.then(check('automargin keep width', {'yaxis.automargin': 'width'}, {
t: '=', l: 'grew',
b: '=', r: '='
}))
.then(check('automargin x', {'xaxis.automargin': true, 'yaxis.automargin': false}, {
t: '=', l: '=',
b: 'grew', r: 'grew'
}))
.then(check('automargin not bottom', {'xaxis.automargin': 'top+width'}, {
t: '=', l: '=',
b: '=', r: 'grew'
}))
.then(check('automargin keep right', {'xaxis.automargin': 'right'}, {
t: '=', l: '=',
b: '=', r: 'grew'
}))
.then(check('automargin keep bottom', {'xaxis.automargin': 'bottom'}, {
t: '=', l: '=',
b: 'grew', r: '='
}))
.then(check('automargin keep top right', {'xaxis.automargin': 'top+right'}, {
t: '=', l: '=',
b: '=', r: 'grew'
}))
.then(check('automargin keep top left', {'xaxis.automargin': 'top+left'}, {
t: '=', l: '=',
b: '=', r: '='
}))
.then(check('automargin keep bottom left', {'xaxis.automargin': 'bottom+left'}, {
t: '=', l: '=',
b: 'grew', r: '='
}))
.then(check('turn off automargin', {'xaxis.automargin': false, 'yaxis.automargin': false}, {
t: '=', l: '=',
b: '=', r: '='
}))
.then(done, done.fail);
});

it('should handle cases with free+mirror axes', function(done) {
Plotly.newPlot(gd, [{
y: [1, 2, 1]
Expand Down
28 changes: 26 additions & 2 deletions test/plot-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -9717,7 +9717,19 @@
"description": "Determines whether long tick labels automatically grow the figure margins.",
"dflt": false,
"editType": "ticks",
"valType": "boolean"
"extras": [
true,
false
],
"flags": [
"height",
"width",
"left",
"right",
"top",
"bottom"
],
"valType": "flaglist"
},
"autorange": {
"description": "Determines whether or not the range of this axis is computed in relation to the input data. See `rangemode` for more info. If `range` is provided, then `autorange` is set to *false*.",
Expand Down Expand Up @@ -10956,7 +10968,19 @@
"description": "Determines whether long tick labels automatically grow the figure margins.",
"dflt": false,
"editType": "ticks",
"valType": "boolean"
"extras": [
true,
false
],
"flags": [
"height",
"width",
"left",
"right",
"top",
"bottom"
],
"valType": "flaglist"
},
"autorange": {
"description": "Determines whether or not the range of this axis is computed in relation to the input data. See `rangemode` for more info. If `range` is provided, then `autorange` is set to *false*.",
Expand Down