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

Remove internal option layout.autosize='initial' (Fixes #537) #577

Merged
merged 26 commits into from
Jun 9, 2016

Conversation

n-riesco
Copy link
Contributor

  • Moved initial call to plotAutoSize into Plots.supplyDefaults(gd).
  • Replaced { autosize: 'initial' } with the flag
    gd._fullLayout._initialAutoSizeIsDone.
  • { autosize: false } the values of width and height undefined in
    gd.layout will be autosized only once.
  • { autosize: true } only autosizes the values of width and height
    undefined in gd.layout.

Fixes #537

* Commit 5df675a (fix demo/outside legend bug and null data autoscale
  bug) introduced a guard in plotAutoSize to avoid calling layoutStyles
  while autosize is set to 'initial'.

* Commit ee974d9 (autosizing in shareplots, autosize aspect ratio
  restrictions and ...) removed the call to layoutStyles but forgot to
  remove the guard.

* This commit removes the guard.

* Checked that all the jasmine and image tests still pass.
* Moved initial call to `plotAutoSize` into `Plots.supplyDefaults(gd)`.

* Replaced `{ autosize: 'initial' }` with the flag
  `gd._fullLayout._initialAutoSizeIsDone`.

* `{ autosize: false }` the values of width and height undefined in
  `gd.layout` will be autosized only once.

* `{ autosize: true }` only autosizes the values of width and height
  undefined in `gd.layout`.

Fixes plotly#537
* Previous image didn't honour the width and height set in the layout.
* Added test to check `Plotly.newPlot` respects `layout.width` and
  `layout.height`.
newWidth,
newHeight;

if(typeof gd.emit === 'function') gd.emit('plotly_autosize');
Copy link
Contributor

Choose a reason for hiding this comment

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

why do we need this?

Copy link
Contributor

Choose a reason for hiding this comment

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

i.e. that if(typeof gd.emit === 'function')

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Otherwise I get errors in the console (perhaps some test are mocking gd with something other than a DOM element?). I will come back to you on this tomorrow.

Copy link
Contributor

Choose a reason for hiding this comment

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

Good call. Plots.supplyDefaults should work with plain objects.

Could we make this check more robust? I'd vote for using Lib.isPlotDiv.

@etpinard etpinard added bug something broken status: in progress labels May 30, 2016

Plotly.plot(gd, data).then(function() {
Plotly.newPlot(gd, data, { height: 50 }).then(function() {
expect(gd._fullLayout.height).toBe(50);
Copy link
Contributor

Choose a reason for hiding this comment

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

we should also check to svg attributes too to make sure that it updated properly.

@etpinard
Copy link
Contributor

etpinard commented May 30, 2016

@n-riesco looks like you're on the right path.

Replaced { autosize: 'initial' } with the flag gd._fullLayout._initialAutoSizeIsDone

Yep. Great idea. 👍

{ autosize: false } the values of width and height undefined in gd.layout will be autosized only once.

Hmm. Is this the current behavior? Shouldn't { autosize: false } lead to layout.width: 700 and layout.height: 400 as per their defaults?

{ autosize: true } only autosizes the values of width and height undefined in gd.layout

Great.

Unfortunately, the plotly.js auto-size routine has very poor test coverage; I'm a little concerned with merging this PR. @n-riesco could make sure that all code paths in plotAutoSize are tested under Plotly.plot and Plotly.relayout?

@n-riesco
Copy link
Contributor Author

On 30/05/16 17:32, Étienne Tétreault-Pinard wrote:

@n-riesco https://github.com/n-riesco looks like you're on the right path.
{ autosize: false } the values of width and height undefined in gd.layout will be autosized only once.

Hmm. Is this the current behavior? Shouldn't |{ autosize: false }| lead to |layout.width: 700| and |layout.height: 400| as per their defaults?

The current behaviour is that if layout.width or layout.height are undefined, then layout.autosize is set to 'initial'.
When layout.autosize is set to 'initial' (and assuming config.fillFrame is false), plotAutosize gets the layout width and height using window.getComputedStyle(gd). Only if this fails, then the default width and height values are used.

Unfortunately, the plotly.js auto-size has very poor test coverage; I'm a little concerned with the PR. @n-riesco https://github.com/n-riesco could make sure that all code paths in |plotAutoSize| https://github.com/plotly/plotly.js/pull/577/files#diff-ad4f76ccd6044ed16514297078e13b84R783 are tested under |Plotly.plot| and |Plotly.relayout|.

OK. I'll add more tests.

@alexcjohnson
Copy link
Collaborator

@n-riesco this is great, that's definitely where the autosize code belongs.

My only question (which goes along with @etpinard 's concern about test coverage - there are tons of cases to consider) is whether there are cases we'd like autosize to coerce to true - ie if width or height is missing? For example if someone is using element size to determine plot size, but has not explicitly set autosize: true - then something outside the plot (window resize or some event on their page) causes the element size to change, I would expect a simple redraw or relayout to resize the plot too.

This then begs the question (that we probably don't need to delve into now, might be a rabbit hole, but something to keep in mind for later) of whether we actually gain anything with the initial / redraw distinction. Either I'm missing something or it used to work differently, but I don't see that plotAutoSize does anything time-consuming, so what's to stop us from always running it if there's one or more missing dimensions or if autosize is explicitly true? There will be places (relayout?) where we need to track whether anything actually changed to decide how much to do next, but we already do that anyway.

@n-riesco
Copy link
Contributor Author

n-riesco commented May 31, 2016

Below are the tests I'm planning to add:

  • autosize:false, fillFrame:true
  • autosize:false, fillFrame:false and frameMargins
  • autosize:false, fillFrame:false and no frameMargins
  • autosize:true, fillFrame:true
  • autosize:true, fillFrame:false and frameMargins
  • autosize:true, fillFrame:false and no frameMargins

For each test, I'll check fullLayout width and height after plot and after relayout.

These tests should cover the main execution paths in plotAutoSize.


Additional tests (from comments):

  • check SVG properties in test 'should respect layout.width and layout.height'
  • test supplyDefaults(gd) can be called with mocked gd.

@etpinard
Copy link
Contributor

@n-riesco your plans sounds good!

* Fix bug in `plotAutoSize`, triggered when `autosize` and `frameMagins`
  are both enabled and `gd` is a plain object.

* Ensure `autosize` doesn't set values of width and height smaller than
  the minimum defined in the corresponding layout attribute.
* Do not do the initial autosize if both config.autosizable and
  layout.autosize are false.
* Fix bug introduced in the commit for respecting `config.autosizable`.
@n-riesco
Copy link
Contributor Author

n-riesco commented Jun 1, 2016

While writing the tests for this PR, I noticed that config accepts the attribute autosizable. In master, this attribute is only honoured by plotPolar. Its meaning in plotPolar is:

  • if config.autosizable: false and layout.autosize: false, then no initial autosize is carried out (if needed, the default values of width and height are used);
  • if config.autosizable: true, then an initial autosize is carried out (regardless of layout.autosize).

I've updated this PR so that autosizable is always honoured.

@n-riesco
Copy link
Contributor Author

n-riesco commented Jun 1, 2016

The PR is ready for review again.

factor = 1 - 2 * frameMargins;

var gdBB;
try {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should avoid using try-catch as much as possible - because they are very slow.

Here too, I'd vote for using Lib.isPlotDiv. But, I'm open to other (possible more strict) suggestions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How about?

var gdBB = fullLayout._container && fullLayout._container.node ?
    fullLayout._container.node().getBoundingClientRect() : {
        width: fullLayout.width,
        height: fullLayout.height
    };

@n-riesco
Copy link
Contributor Author

n-riesco commented Jun 7, 2016

@etpinard About autosizable, I think it should be kept in Plotly v1, and removed in Plotly v2. The reason why I think it should be removed is that I don't think autosizable should be a global setting (different plots should be able to define different values of autosizable).

For Plotly v2, I would suggest to combine autosize and autosizable into a single setting. How about something like this?

{
    autosize: {
        valType: 'enumerated',
        role: 'info',
        values: ['never', 'initial', 'always'],
        description: [
            'Determines how a layout width or height',
            'that has been left undefined by the user',
            'is set.',

            'If *never*, the default values of width and height will be used.',
            'If *initial*, an undefined layout width or height',
            'will be initialized on the first call to plot.'
            'If *always*, an undefined layout width or height',
            'will be set on the first call to plot and subsequent calls to relayout.'
        ].join(' ')
    }
}

@n-riesco
Copy link
Contributor Author

n-riesco commented Jun 7, 2016

PR ready for review again.

} catch(err) {
gdBB = {
var gdBB = fullLayout._container && fullLayout._container.node ?
fullLayout._container.node().getBoundingClientRect() : {
Copy link
Contributor

Choose a reason for hiding this comment

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

Good. 👍

@etpinard
Copy link
Contributor

etpinard commented Jun 7, 2016

Thanks @n-riesco . you're almost there.

As mentioned previously, this PR was set uncover a lot of un-tested code. Commits 172d81d and 862578e were no exceptions.

Would you mind a few test cases (one per commit is fine) for the two commits above? To lock their behavior until v2.0.0.

@n-riesco
Copy link
Contributor Author

n-riesco commented Jun 7, 2016

I've added tests for the commits you suggested and this uncovered an issue with 862578e and another with Lib.isPlotDiv. Both are now fixed and tested.

The PR is ready for review again.

@etpinard
Copy link
Contributor

etpinard commented Jun 9, 2016

Great. I tested your branch and I couldn't find any edge cases that you might have missed. 🎉

This for sure wins the PR of the week 🏆

I'll make two minor comments on the line diff (nothing major) and then it'll be time to 💃 .

@@ -47,11 +47,16 @@ module.exports = {
autosize: {
valType: 'enumerated',
role: 'info',
// TODO: better handling of 'initial'
values: [true, false, 'initial'],
values: [false, true],
Copy link
Contributor

Choose a reason for hiding this comment

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

we should change the valType to 'boolean' and remove the values field.

@etpinard etpinard merged commit 25a8d9c into plotly:master Jun 9, 2016
@n-riesco n-riesco deleted the remove-autosize-initial branch June 10, 2016 08:04
@@ -326,7 +383,7 @@ describe('Test Plots', function() {
beforeEach(function(done) {
gd = createGraphDiv();

Plotly.plot(gd, [{ x: [1, 2, 3], y: [2, 3, 4] }], {})
Plotly.plot(gd, [{ x: [1, 2, 3], y: [2, 3, 4] }], { autosize: true })
Copy link
Contributor

Choose a reason for hiding this comment

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

@n-riesco , @chriddyp and I are testing the mapbox branch (which is rebased on top of the latest master) in the plot.ly workspace at the moment and we some issues.

Looks like this PR here is backward incompatible and the line ⏫ is a symptom.

More clearly,

Plotly.plot(gd, data);

should go through the plotAutoSize routine on the initial Plotly.plot call.

I believe changing the autosize attribute dflt to true would solve this. What are your thoughts on that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@etpinard An alternative that would be fully backwards-compatible is that Plot.resize(gd) deletes gd.layout.width and gd.layout.height and calls Plotly.relayout(gd, { autosize: true });. I'll prepare a PR so that you can check that it'd fix the issue in the mapbox branch.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See #629

@etpinard
Copy link
Contributor

As per-slack conversation with @n-riesco , I'm reverting this PR and will spend more time testing in plot.ly's workspace before the next minor release - to make sure we handle all edge cases in a backward-compatible way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug something broken
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants