fix: keep reversed axes reversed and sort reversed cube viewports - #117
Merged
Merged
Conversation
jvdd
force-pushed
the
fix/log-axis-viewport
branch
from
September 27, 2026 22:29
6abd79c to
22adf68
Compare
jvdd
force-pushed
the
fix/axis-handling-gaps
branch
from
September 27, 2026 22:29
00330a7 to
0d35a63
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes items 1 and 4 of #81. Item 3 was already fixed on
mainby d370e8f (testTestAgentReadback::test_apply_updates_the_lock_controls). Item 2 is covered by #110. Item 5 needs a design and is not in this PR.Change
syncLayoutViewportsetautorange: trueon every axis without a viewport key, so a reversed axis rendered normal. A newdeclaredAutorangereads the figure layout from the spec (Plotly writesautorange: trueback into the layout it draws) and follows the rule of_axis_reversedinspec.py: any autorange that contains"reversed", or a fixed range given high to low, gives"reversed". The fixed bounds are still dropped, as before. So"min reversed"and"max reversed"also give"reversed": without their fixed end, Plotly draws them at a default range.orientedRangeorders every range the client writes to Plotly (syncLayoutViewportandfvApplyAxisLocks) by the declared direction. A stored viewport or lock range holds only its bounds, so one given low to high, for example throughflexvizApply, still draws a reversed axis reversed.FlexEngine.build_cubesnow normalizes reversed viewport pairs, likeFlexEngine.process. Both call one helper,_normalize_viewports, first._fvCubeViewportDomain) sorts the pair too. Without this, the key of a reversed source never matched the sorted server header.fvApplyAxisLocksloses its unusedchangedAxisIdparameter and branch. No caller passed it, and the branch wroteautorange: true.Architecture.mddescribes reversed axes and the ingestion step.Impact
fig.update_layout(xaxis={"autorange": "reversed"})(or"min reversed","max reversed", or a fixed range given high to low) renders reversed on the first render, and stays reversed after a zoom, a panel reset, a double-click autorange, a global reset and an axis lock.lo > hi, so the preview and the commit showed no rows, and with only the server fix the client would refuse every cube for that source.Tests
TestReversedAxisBrowser::test_reversed_axis_survives_zoom_and_reset, forautorange: "reversed", forrange: [999, 0], and for"min reversed"and"max reversed"with a fixed end. The first two fail before the item 1 commit, the last two before the commit that makes them"reversed".TestReversedAxisBrowser::test_range_given_low_to_high_draws_reversed, for a viewport and for a lock range set throughflexvizApply. Both fail before theorientedRangecommit.test_cube_server.py::test_reversed_viewports_resolve_like_ascending_ones: the cube header for reversed viewports equals the header for ascending ones. Fails before the fix.test_browser_cube.py::test_reversed_zoomed_source_live_updates: a live brush on a reversed, zoomed source sends one cube request and filters the target to some of its rows. Fails before the fix (the target sums to 0).main:pytest tests: 2290 passed, 1 skipped.pytest -m browser tests: 330 passed, 12 skipped.