Skip to content

[BUG]: error_y.array renders error bars before initial render but not after Plotly.restyle #5621

@ym-xie

Description

@ym-xie

Description

A scatter trace renders error_y.array differently depending on when the property is
applied.

If error_y.array is set before the first render, vertical error bars are visible. If
the same base figure is rendered first and the same error_y.array value is applied
later with Plotly.restyle, the error bars are not shown.

Screenshots/Video

Image Image

Steps to reproduce

import io

import pandas as pd
import plotly
import plotly.express as px
import plotly.io as pio
from PIL import Image, ImageChops
from playwright.sync_api import sync_playwright


print(f"Plotly version: {plotly.__version__}")


def make_fig():
    df = pd.DataFrame(
        {
            "x": [3.0, 3.2, 3.4],
            "y": [5.0, 4.8, 5.4],
            "color": [5.0, 4.8, 5.4],
        }
    )
    return px.scatter(
        df,
        x="x",
        y="y",
        color="color",
        color_continuous_scale=px.colors.sequential.Viridis,
    )


error_array = [0.0, 0.5, 1.0]

# Case 1: set error_y.array before the initial render.
fig1 = make_fig()
fig1.data[0].error_y.array = error_array
assert tuple(fig1.data[0].error_y.array) == tuple(error_array)
html1 = pio.to_html(fig1, full_html=True, include_plotlyjs=True)

# Case 2: render first, then apply the same value with Plotly.restyle.
fig2 = make_fig()
html2 = pio.to_html(fig2, full_html=True, include_plotlyjs=True)
html2 = html2.replace(
    "</body>",
    """
<script>
window.addEventListener("DOMContentLoaded", () => {
    const plotEl = document.getElementsByClassName("plotly-graph-div")[0];
    setTimeout(() => {
        Plotly.restyle(plotEl, {"error_y.array": [0.0, 0.5, 1.0]}, [0]);
    }, 1000);
});
</script>
</body>""",
)

with sync_playwright() as playwright:
    browser = playwright.chromium.launch(
        headless=True,
        args=["--no-sandbox", "--disable-dev-shm-usage", "--disable-gpu"],
    )
    page = browser.new_page(viewport={"width": 1200, "height": 800})

    page.set_content(html1, wait_until="domcontentloaded")
    page.wait_for_timeout(1500)
    img1 = page.screenshot(path="plot1.png")

    page.set_content(html2, wait_until="domcontentloaded")
    page.wait_for_timeout(2000)
    img2 = page.screenshot(path="plot2.png")

    browser.close()

diff = ImageChops.difference(
    Image.open(io.BytesIO(img1)).convert("RGB"),
    Image.open(io.BytesIO(img2)).convert("RGB"),
)

print("Saved plot1.png (Python-side before render)")
print("Saved plot2.png (JS-side restyle after render)")
assert diff.getbbox() is not None, "Reproducer did not reproduce: images are identical."
print("Bug reproduced: the two renderings differ.")

Notes

Add info here that doesn't fit in the other sections.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugsomething broken

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions