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
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
35 changes: 35 additions & 0 deletions src/plots/cartesian/axes.js
Original file line number Diff line number Diff line change
Expand Up @@ -2622,6 +2622,8 @@ axes.drawOne = function(gd, ax, opts) {
rangeSliderPush = Registry.getComponentMethod('rangeslider', 'autoMarginOpts')(gd, ax);
}

keepSelectedAutoMargin(ax.automargin, push);
Copy link
Contributor

Choose a reason for hiding this comment

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

So keepSelectedAutoMargin mutates the push variable.
It was rather difficult to figure out what this step does here.
How about refactoring like this:

        if(typeof automargin === 'string') filterPush(push, ax.automargin);

On another note, wondering if mutating push should be performed earlier in the process so that the value in mirrorPush would be accurate?

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't see the point of using a mirror if the result would still be cut off. So there is no need to filter the mirrorPush.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Actually I think it's correct to filter mirrorPush as well. The whole mirrorPush construction is inside the if(ax.automargin) block - implying that if you disable automargin, we're not going to push the margins for mirror axes regardless of whether they end up cut off. Therefore you should be able to choose which directions this applies to just as you can the regular axis automargin.


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

function keepSelectedAutoMargin(automargin, push) {
if(typeof automargin === 'boolean') return push;

var keepMargin = [];
var mapping = {
archmoj marked this conversation as resolved.
Show resolved Hide resolved
width: ['x', 'r', 'l', 'xl', 'xr'],
height: ['y', 't', 'b', 'yt', 'yb'],
right: ['r', 'xr'],
left: ['l', 'xl'],
top: ['t', 'yt'],
bottom: ['b', 'yb']
};

Object.keys(mapping).forEach(function(key) {
if(automargin.indexOf(key) !== -1) {
mapping[key].forEach(function(item) {
keepMargin.push(item);
});
}
});

Object.keys(push).forEach(function(key) {
if(keepMargin.indexOf(key) === -1) {
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