Layout ignores range on x-axis

Dear community,

I am observing inconsistent behavior of a figure plotted within Dash/dcc.Graph.

Intention:
My figures have high height-to-width ration (they are very tall) and the user is expected to scroll through them. The scrolling is on the Dash side, when a html.Div(…, style={“overflow-y”:“scroll”}). In order to display the figure properly, the figure should occupy the full width of the Div (the height is to be absorbed by the scroll mechanism). To keep the displayed traces appropriately sized, the width of the figure, or in plotly terms the range of xaxis, needs to be set and preserved → figure spanning the width of the Div → set range spanning the width of the figure.

Observation:
Figure plotted within dcc.Graph does NOT preserve the x-axis range when the figure has high height-to-width ratio.

Reproducible example:

from dash import Dash, dcc, html
from plotly import graph_objects as go

app = Dash(__name__)


traces = [
        go.Scatter(
            x=[1,2,3,4], 
            y=[10, 20, 30, 100],
            )
        ]


layout = go.Layout(
    autosize=True,
    xaxis=dict(
            type="linear",
            range=[0, 5],
            ),
    yaxis=dict(
            type="linear",
            # range=[0, 120],
            scaleanchor="x",
            # scaleratio=1.0,
               ),
)

fig = go.Figure(data=traces, layout=layout)


graph_config = \
    dict(
        responsive=False,
        displayModeBar=True,
    )


graph = \
    html.Div(
        children=[
            html.Div(
                children=[
                    html.Div(
                        children=[
                            dcc.Graph(
                                id='example-graph',
                                figure=fig,
                                config=graph_config,
                                )
                        ],
                        style={
                            "width":'100%', 
                            "height":'100%', 
                            "overflowY":"scroll", # <------- small correction

                            },
                    ),
                ],
                style=dict(width="100%", height='calc( 100% - 10vh )'),
            ),
        ],
        style=dict(width='calc( 100% - 10vh )'),
    )


app.layout = html.Div(children=\
    [html.Div(children=[graph],
            style=dict(width="50vh", height="50vh")),
    ],
)


if __name__ == "__main__":
    app.run_server(debug=True)

I have ran several experiments, experimenting with layout options and dcc.Graph config (feel free to suggest better settings). The only time when the range of the x-axis is implemented, is when the layout settings for y-axis DO NOT implement ‘scaleanchor=“x”’. This, of course, results in a figure that does not have defined aspect ration, thus is the html.Div is not scrollable.

Question:
Is there any way to have a tall figure with predefined xaxis range, such that the parent html.Div becomes scrollable on overflow-y?

P.S.
Using fixed layout.width and layout.height does not enforce the xaxis range. On the contrary, the figure breaks the xaxis range be being extremely zoomed out.


UPDATE

Probing the x-axis range implementation using JavaScript. Minimalistic example implementing a main.py and assets/changeFigureAxisRange.js.

Expected behavior would be that the figure has xaxis range [0. 5], yaxis range [0, 120], the size of the figure is of the same ratio 1200:50, thus causing the parent html.Div to overflow in y-direction.

Observation:
Figure updated using JavaScript after the figure is fully loaded, such that the layout is updated to new x-axus range and y-axis range (together with scaleanchor and scaleratio), does NOT retults in a properly scaled figure following the prescribed ranges. If the width and height are updated, the figure explodes, the ranges are not followed at all.

Reproducible example:

<main.py>

from dash import Dash, dcc, html
from plotly import graph_objects as go

app = Dash(__name__)


traces = [
        go.Scatter(
            x=[1,2,3,4], 
            y=[10, 20, 30, 100],
            )
        ]


layout = go.Layout(
    autosize=True,
    xaxis=dict(
            type="linear",
            range=[0, 5],
            ),
    yaxis=dict(
            type="linear",
            # range=[0, 120],
            scaleanchor="x",
            scaleratio=1.0,
               ),
)

fig = go.Figure(data=traces, layout=layout)


graph_config = \
    dict(
        # responsive=False,
        # displayModeBar=True,
    )


graph = \
    html.Div(
        children=[
            html.Div(
                children=[
                    html.Div(
                        children=[
                            dcc.Graph(
                                id='fig-graph',
                                figure=fig,
                                config=graph_config,
                                )
                        ],
                        style={
                            "width":'100%', 
                            "height":'100%', 
                            "overflowY":"scroll",

                            },
                    ),
                ],
                style=dict(width="100%", height='calc( 100% - 10vh )'),
            ),
        ],
        style=dict(width='calc( 100% - 10vw )', height="50vh"),
    )


app.layout = html.Div(children=\
    [html.Div(children=[graph],
            style=dict(width="50vw", height="50vh")),
    ],
)


if __name__ == "__main__":
    app.run_server(debug=True)

assets/<changeFigureAxisRange.js>

(function() {
    function checkGraphLoaded() {
        const figGraph = document.getElementById('fig-graph');
        if (figGraph && window.Plotly) {
            Plotly.newPlot(figGraph, figGraph.data, figGraph.layout).then(function() {
                console.log('fig-graph is fully loaded');
                updateGraphAxisRanges([0, 5], [0, 120]);
                clearInterval(intervalId);
            }).catch(function(error) {
               console.error('Error while initializing Plotly graph:', error);
            });
        } else {
            console.log('Waiting for fig-graph to load fully...');
        }
    }

    let intervalId = setInterval(checkGraphLoaded, 1000); 

    function updateGraphAxisRanges(newXRange, newYRange) {
        const graphDiv = document.getElementById('fig-graph');
        if (graphDiv) {
            Plotly.relayout(graphDiv, {
                'height':1200,
                'width':50,
                'xaxis.range': newXRange,
                'yaxis.range': newYRange,
                'yaxis.scaleanchor': 'x',
                'yaxis.scaleratio': 1.0
            }).then(function() {
                console.log('Axis ranges updated:', newXRange, newYRange);
            }).catch(function(error) {
                console.error('Failed to update axis ranges:', error);
            });
        } else {
            console.error('Graph element not found');
        }
    }
})();

at least 10 characters to get a bump on the topic

Hi, welcome to the forum.

I’m a bit unsure, but I think it might hinge on how the figure is sized. If you want your x range to be [-5, 5] and to fill the figure width, and you want your y range to be [0,100] and to fit within the figure, and you want x and y to be shown at the same scale, then (inevitably) the pixel height of the figure has to be ten times its pixel width.

I don’t know any combination of settings that enforces that in any automatic way, beyond setting a fixed width and height for the figure.

If you also want the width of the figure to adjust to the viewport or container width, and then want the height of the figure to adjust to keep a fixed ratio of height/width, then I’ve only got a tentative suggestion that the key to this might be adapting the code that @AIMPED posted here: