plotly.js icon indicating copy to clipboard operation
plotly.js copied to clipboard

full_figure_for_development giving different margins in 6.3 than in 6.2

Open my-tien opened this issue 3 months ago • 4 comments

I noticed that full_figure_for_development gives different margins in plotly.py 6.2 and 6.3, probably because of the plotly.js version bump from 3.0.1 to 3.1.0. Can this be considered a bug? In our use case, we read and use these values to update figure.layout.height, because we want to ensure a fixed height of the plot area.

Consider this plotly.py code:

import plotly.graph_objects as go

figure_dict = {
  "data": [
    {
      "x": ["A", "B", "C", "D"],
      "y": [100, 200, 150, 300],
      "type": "bar"
    }
  ],
  "layout": {
    "margin": {
      "b": 40,
      "l": 0,
      "r": 0,
      "t": 0
    },
    "yaxis": {
      "tickfont": {
        "size": 19
      },
      "ticklabelposition": "inside top",
      "ticklen": 10
    }
  }
}

figure = go.Figure(data=figure_dict["data"], layout=figure_dict["layout"])
fffd = figure.full_figure_for_development()

print(fffd.layout.computed["margin"])

Output for 6.2.0:

{'b': 105, 'l': 0, 'r': 0, 't': 4}

Output for 6.3.0:

{'b': 105, 'l': 0, 'r': 0, 't': 0}

With this second example, also margin.b differs:

import plotly.graph_objects as go

figure_dict = {
    "data": [
        {
            "x": [
                "A",
                "B",
                "C",
                "D"
            ],
            "y": [
                100,
                200,
                150,
                190
            ],
            "type": "bar"
        }
    ],
    "layout": {
        "yaxis": {
            "automargin": "height",
            "ticklabelposition": "inside top",
            "insiderange": [
                0,
                250
            ]
        },
        "autosize": True,
        "legend": {
            "orientation": "v",
            "x": 0.05,
            "y": -0.1,
            "yanchor": "top"
        },
        "margin": {
            "autoexpand": True,
            "b": 40,
            "l": 0,
            "r": 0,
            "t": 0
        },
        "showlegend": True
    }
}

figure = go.Figure(data=figure_dict["data"], layout=figure_dict["layout"])
fffd = figure.full_figure_for_development()

print(fffd.layout.computed["margin"])

Output for 6.2.0:

{'b': 70, 'l': 0, 'r': 0, 't': 17}

Output for 6.3.0:

{'b': 72, 'l': 0, 'r': 0, 't': 0}

my-tien avatar Nov 13 '25 13:11 my-tien

Hi @my-tien, this is very interesting, thanks for reporting.

I tried to reproduce on my own machine and I got slightly different numbers for the first figure (see below) but in any case, the effect is the same: the top margin was a nonzero value in 6.2.0, and is now a zero value in 6.3.0. And also the bottom margin has increased slightly for the second figure.

First figure:
6.2.0: {'b': 40, 'l': 0, 'r': 0, 't': 4}
6.3.0: {'b': 40, 'l': 0, 'r': 0, 't': 0}

Second figure:
6.2.0: {'b': 70, 'l': 0, 'r': 0, 't': 17}
6.3.0: {'b': 72, 'l': 0, 'r': 0, 't': 0}

I made a Codepen which confirms that this change is indeed caused by the switch from Plotly.js 3.0.1 to 3.1.0

In general I would say that we try to avoid changes like this, or at least be aware of them, and this is the type of thing that is typically caught by our image tests. However it's possible that small margin changes may occur from time to time, if we think that the change is justified.

In this case I do think that the 6.3.0 output seems 'better' in the sense that it's more faithful to the user-supplied margins, do you agree?

I would have to investigate further to narrow down what specific PR introduced this change, but since your example figures include "ticklabelposition": "inside top", I have a hunch that it might be related to this PR: https://github.com/plotly/plotly.js/pull/7417 . That's just a guess though, I haven't looked into it enough to be sure.

emilykl avatar Nov 14 '25 00:11 emilykl

Hi @emilykl, thx for testing and for sharing that Codepen. I can confirm that after my PR you mentioned the change occurs. I figured out how to test this in plotly.js directly (If you want, we can also move the issue there). And so I was able to go back to the exact commit and test before and after. Find my test code below.

I think it makes sense and is good that the top margin vanishes, however I am confused that the bottom margin becomes larger. This might be a bug/behavior that is now revealed?

figure = {
    "data": [
        {
            "x": [
                "A",
                "B",
                "C",
                "D"
            ],
            "y": [
                100,
                200,
                150,
                190
            ],
            "type": "bar"
        }
    ],
    "layout": {
        "yaxis": {
            "automargin": "height",
            "ticklabelposition": "inside top",
            "insiderange": [
                0,
                250
            ]
        },
        "autosize": true,
        "legend": {
            "orientation": "v",
            "x": 0.05,
            "y": -0.1,
            "yanchor": "top"
        },
        "margin": {
            "autoexpand": true,
            "b": 40,
            "l": 0,
            "r": 0,
            "t": 0
        },
        "showlegend": true
    }
}

Plotly.newPlot('plotly_div', figure.data, figure.layout).then(
    function(gd) {
        Plotly.toImage(gd, { format: "full-json", imageDataOnly: true })
        .then(
            function(full_figure) {
                console.log("FFFD", JSON.parse(full_figure).layout.computed["margin"]);
            }
        )
    }
);

With this code I get:

Before https://github.com/plotly/plotly.js/pull/7417

{ b: 66, l: 0, r: 0, t: 17 }

After https://github.com/plotly/plotly.js/pull/7417

{ b: 67, l: 0, r: 0, t: 0 }

EDIT: Btw. what is that template you use in your codepen? It seems really useful to visualize the "issue". Is that your usual template for debugging things?

my-tien avatar Nov 14 '25 10:11 my-tien

Yes now that we've narrowed down the issue to Plotly.js I'll move the issue over.

I'm not sure why the bottom margin gets larger. That does seem unintended, but I also know that the automargin logic is pretty complex and tracking down the exact reason could be a deep rabbit hole.

This is the template I use for creating Plotly.js charts in Codepen! In this case, to reproduce your issue, I got the full JSON string representation of figure (json.dumps(figure, cls=plotly.utils.PlotlyJSONEncoder, indent=2)) and then copy-pasted it into the Codepen.

emilykl avatar Nov 14 '25 21:11 emilykl

Agree regarding the rabbit hole… I'm shelving this for now.

my-tien avatar Nov 17 '25 16:11 my-tien