Skip to content

Commit 083b847

Browse files
committed
Fix to_html responsive height by styling outer wrapper div
The HTML produced by `to_html` wraps the figure in an outer `<div>` with no inline style. The inner figure div sets `height:100%`, but because the wrapper has no height, that 100% resolves to zero and plotly.js falls back to its hardcoded 450px. The result is that the documented default of `default_height='100%'` cannot inherit from a sized parent container, so figures embedded in responsive layouts (Jinja templates, Dash containers with `vh`/`%` heights, etc.) silently render at 450px. Move the requested dimensions onto the wrapper and let the inner figure div fill it. Pixel defaults still produce a fixed-size figure; percentage defaults now propagate from the parent container as the docstring claims. Closes #5591
1 parent 7827926 commit 083b847

3 files changed

Lines changed: 50 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
1212
- Use presence of `COLAB_NOTEBOOK_ID` env var to enable Colab renderer instead of testing import of `google.colab` [[#5473](https://github.com/plotly/plotly.py/pull/5473)], with thanks to @kevineger for the contribution!
1313
- Update tests to be compatible with numpy 2.4 [[#5522](https://github.com/plotly/plotly.py/pull/5522)], with thanks to @thunze for the contribution!
1414
- Add default headers to be passed in to Kaleido v1.3.0 to avoid blocked Open Street Map tiles [#5588](https://github.com/plotly/plotly.py/pull/5588)]
15+
- Propagate the requested `default_height`/`default_width` to the outer wrapper div produced by `to_html` so that responsive (percentage) dimensions inherit from a sized parent container instead of collapsing to the plotly.js 450px fallback [[#5591](https://github.com/plotly/plotly.py/issues/5591)]
1516

1617
### Updated
1718
- The `__eq__` method for `graph_objects` classes now returns `NotImplemented` to give the other operand an opportunity to handle the comparison [[#5547](https://github.com/plotly/plotly.py/pull/5547)], with thanks to @RazerM for the contribution!

plotly/io/_html.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,11 +318,11 @@ def to_html(
318318
)
319319

320320
plotly_html_div = """\
321-
<div>\
321+
<div style="height:{height}; width:{width};">\
322322
{mathjax_script}\
323323
{load_plotlyjs}\
324324
<div id="{id}" class="plotly-graph-div" \
325-
style="height:{height}; width:{width};"></div>\
325+
style="height:100%; width:100%;"></div>\
326326
<script>\
327327
window.PLOTLYENV=window.PLOTLYENV || {{}};{base_url_line}\
328328
{script};\

tests/test_io/test_html.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,53 @@ def test_cdn_includes_integrity_attribute(fig1):
5353
assert match is not None, "CDN script tag with integrity attribute not found"
5454

5555

56+
def test_outer_wrapper_carries_requested_dimensions(fig1):
57+
"""
58+
Regression test for https://github.com/plotly/plotly.py/issues/5591.
59+
60+
The outer wrapper div produced by ``to_html`` must carry the requested
61+
height/width so that percentage values propagate from a parent container
62+
down to the figure element. Without this, a default of ``height='100%'``
63+
collapses to zero (because the wrapper has no height) and plotly.js falls
64+
back to its hardcoded 450px, breaking any responsive layout.
65+
"""
66+
html = pio.to_html(fig1, include_plotlyjs="cdn", full_html=False)
67+
68+
wrapper_match = re.match(r'<div style="([^"]+)">', html)
69+
assert wrapper_match is not None, (
70+
"Outer wrapper div is missing inline style; responsive parents cannot "
71+
"propagate dimensions to the figure."
72+
)
73+
wrapper_style = wrapper_match.group(1)
74+
assert "height:100%" in wrapper_style
75+
assert "width:100%" in wrapper_style
76+
77+
inner_match = re.search(
78+
r'<div id="[^"]+" class="plotly-graph-div" style="([^"]+)">',
79+
html,
80+
)
81+
assert inner_match is not None
82+
assert "height:100%" in inner_match.group(1)
83+
assert "width:100%" in inner_match.group(1)
84+
85+
86+
def test_outer_wrapper_respects_explicit_pixel_dimensions(fig1):
87+
"""Explicit pixel dimensions must reach the outer wrapper unchanged."""
88+
html = pio.to_html(
89+
fig1,
90+
include_plotlyjs="cdn",
91+
full_html=False,
92+
default_width=600,
93+
default_height=400,
94+
)
95+
96+
wrapper_match = re.match(r'<div style="([^"]+)">', html)
97+
assert wrapper_match is not None
98+
wrapper_style = wrapper_match.group(1)
99+
assert "height:400px" in wrapper_style
100+
assert "width:600px" in wrapper_style
101+
102+
56103
def test_cdn_integrity_hash_matches_bundled_content(fig1):
57104
"""Test that the SRI hash in CDN script tag matches the bundled plotly.js content"""
58105
html_output = pio.to_html(fig1, include_plotlyjs="cdn")

0 commit comments

Comments
 (0)