Skip to content

Keep dcc.Graph edits across figure updates when uirevision is unchanged - #4102

Open
maoxor wants to merge 2 commits into
plotly:devfrom
maoxor:graph-uirevision-keeps-zoom
Open

maoxor wants to merge 2 commits into
plotly:devfrom
maoxor:graph-uirevision-keeps-zoom

Conversation

@maoxor

@maoxor maoxor commented Oct 29, 2026

Copy link
Copy Markdown

Fixes #4093

Since #3882, the plotly_relayout handler copies user edits (zoom, pan, ...) into the figure prop so that a later Patch doesn't revert them. That prop change makes the graph re-plot with the edits as figure input. plotly.js then treats them as the new starting point and drops them from its uirevision bookkeeping (_preGUI), so the next figure that leaves them out resets the view, even when uirevision hasn't changed.

So with uirevision=True, zooming and then returning a new figure from a callback loses the zoom (the report in #4093). Zooming, applying a Patch, and then returning a new figure loses it too.

Fix

When a layout key is synced, remember the value it had before the edit and the uirevision at that time. When the graph plots a figure that still holds the synced value under the same uirevision, it gives plotly.js the value from before the edit instead. plotly.js then sees an unchanged input and keeps the edit itself, as it did before #3882. The figure prop keeps the synced edits, so #3810 stays fixed.

This is only done for edits that plotly.js keeps under uirevision (layoutUIControlPatterns in plotly.js plot_api.js: axis ranges, shapes, annotations, dragmode, map center, ...). For other edits, like an edited legend title, putting the old value back would undo the user's edit right away, so those keys keep the current behavior.

Testing

  • test_grbs013: zoom with a real mouse drag, then return a new figure, then Patch and return a new figure again; the zoom stays. It fails on dev (range back to autorange) and passes with the change.
  • test_grbs014: an edit that uirevision does not track (legend title) is not undone by the sync or by a later Patch. I checked that it fails if the "edits plotly keeps" check is removed.
  • tests/integration/graph: 53 passed, 1 skipped, run twice. Jest in dash-core-components: 85 passed.

I also ran user journeys in headless Chrome against the published 4.4.0 and 4.4.1 wheels and against this branch. All with uirevision=True unless noted. Every zoom and pan is a real mouse drag:

Journey 4.4.0 4.4.1 this PR
zoom, new figure kept lost kept
zoom, zoom, new figure kept lost kept
zoom, new figure, zoom, new figure kept lost kept
zoom, Patch, new figure kept lost kept
zoom, Patch, Patch, new figure, new figure kept lost kept
zoom, double-click, new figure autorange autorange autorange
zoom, new figure, double-click, new figure autorange lost at the first new figure autorange
zoom: figure prop holds the zoomed range no yes yes
no uirevision: zoom, Patch lost (#3810) kept kept
no uirevision: zoom, new figure reset reset reset

On this branch, switching to pan from the modebar and panning a scattermap (map center) also survive a new figure and a Patch followed by a new figure.

The added work per plot is one equals on each key the user edited. It costs less than the clone that getLayout already does on the same layout: about 1.5 us for an axis, and about 1 ms for 1000 edited shapes.

Contributor Checklist

  • I have broken down my PR scope into the following TODO tasks
    • Give plotly.js the pre-edit value of synced layout keys while uirevision is unchanged
    • Leave edits that plotly.js does not keep under uirevision alone
  • I have run the tests locally and they passed. (refer to testing section in contributing)
  • I have added tests, or extended existing tests, to cover any new features or bugs fixed in this PR

optionals

  • I have added entry in the CHANGELOG.md

Since plotly#3882 the plotly_relayout handler copies user edits (zoom, pan,
...) into the figure prop. The graph then re-plots with them as figure
input, so plotly.js takes them as the new starting point and drops them
from its uirevision bookkeeping. The next figure that leaves them out
resets the view, even with the same uirevision.

Remember the value each synced layout key had before the edit, and
while the figure still holds the synced value under the same uirevision,
give that value to plotly.js instead, so plotly.js keeps the edit
itself. Only do this for edits that plotly.js keeps under uirevision,
so other edits are not undone.

Fixes plotly#4093
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] dcc.Graph loses zoom state with unchanged uirevision

1 participant