Skip to content

fix(routing): let Back leave the pathway browser - #184

Merged
adamjohnwright merged 1 commit into
mainfrom
fix/back-button-in-pathway-browser
Sep 9, 2026
Merged

fix(routing): let Back leave the pathway browser#184
adamjohnwright merged 1 commit into
mainfrom
fix/back-button-in-pathway-browser

Conversation

@adamjohnwright

Copy link
Copy Markdown
Contributor

Opening a pathway cost three presses of Back to get out of — and the first two did something nobody asked for.

The app writes its own defaults into the URL, so ?tab=info and then ?tab=details each became a history entry. Pressing Back stepped through tab changes the reader never made before it would leave the page:

on the pathway   /PathwayBrowser/R-HSA-109606?tab=details
Back #1       -> ?tab=info      (a tab nobody chose)
Back #2       -> (no tab)
Back #3       -> /about/news

The fix

The URL still has to carry the tab — a shared link has to open on the same thing — so the fix isn't to stop writing it. Being given a tab replaces the entry; choosing one still adds to it.

details.component already knew the difference — it captures tabCameFromUrl to tell an explicit choice from a default. There was just no way to tell the URL writer, so settle() is that way.

before after
presses of Back to leave 3 1
tab still in the URL yes yes
Back after choosing a tab returns returns

Why it's here

I found this while reviewing #183, called it pre-existing, and left it as needing a product decision — my stated reason being that making normalisation replace would break Back between pathways.

That reason was wrong. It didn't need changing navigation semantics at all, just one flag on the writes that were already identifiable as defaults. Pathway-to-pathway navigation still pushes and is untouched.

Both halves are tested, because keeping the second is what stops the first turning into "Back does nothing in this app".

🤖 Generated with Claude Code

Opening a pathway cost three presses of Back to get out of, and the first
two did something nobody asked for. The app writes its own defaults into the
URL, so `?tab=info` and then `?tab=details` each became a history entry:
pressing Back stepped through tab changes the reader never made before it
would leave the page.

The URL still has to carry the tab -- a shared link has to open on the same
thing -- so the fix is not to stop writing it. Being *given* a tab now
replaces the entry; *choosing* one still adds to it. `details.component`
already knew the difference, in `tabCameFromUrl`; there was just no way to
tell the URL writer, so `settle()` is that way.

From the news archive: one press of Back leaves, where it was three. Choosing
the Molecule tab and pressing Back still returns to Details.

Found while reviewing #183, where I called this pre-existing and left it. It
was pre-existing, and it turned out to be one flag rather than the rework of
navigation semantics I assumed -- the reason I gave for not doing it was
wrong.

Both halves are tested, because keeping the second is what stops the first
turning into "Back does nothing in this app".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@adamjohnwright
adamjohnwright enabled auto-merge (squash) September 9, 2026 18:53
@adamjohnwright
adamjohnwright merged commit 6bb5cf7 into main Sep 9, 2026
6 checks passed
@adamjohnwright
adamjohnwright deleted the fix/back-button-in-pathway-browser branch September 9, 2026 18:59
adamjohnwright added a commit that referenced this pull request Sep 9, 2026
…185)

* fix(routing): decide "replace or add" from the URL, not from a flag

#184 put a `settling` boolean on the service: settle() raised it, the
writer effect read it and lowered it. That only works if the writer
always runs, once, right after. It does not:

  - the writer returns early on the content and search routes, before it
    ever lowers the flag;
  - a default that already matches the current value changes no signal,
    so the writer never runs at all.

Either way the flag outlives its turn, and the reader's next write -- a
real choice -- silently replaces their history instead of adding to it.

So the writer now decides from the values. settle() records the params
the URL should say once its change has been applied; the writer replaces
only if it is about to write exactly those, and clears the record either
way. Both sides go through one currentQueryParams(), so they agree by
construction rather than by a comment.

The record is taken untracked. settle() is called from inside the
details defaulting effect, so reading every param made that effect
depend on every param: choosing the Molecule tab re-ran the default,
which set the tab straight back to details. The URL never changed and
the click did nothing -- caught by e2e/back-button.spec.ts, which now
passes both ways again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs(hierarchy): write down why the scroll restore takes one frame

#181 restores the reader's scroll position on frame one; RevealDirective
brings a selected row into view on frame two. That order is what makes
`block: 'nearest'` compare against where the reader actually was, so a
row already on screen is left alone. Reverse it and clicking a visible
row scrolls twice again -- the bug #181 fixed.

Neither side said so, and both comments read as if their frame count
were a local choice. Noted in both, because either one being
"simplified" breaks the other.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant