Preserve the active concept view when selecting a class (#533) - #536
Conversation
Selecting a different class in the tree reloads the concept_show turbo-frame, and _show.html.haml hard-coded selected:true on the Details tab, so the view always reset to Details regardless of which view (Visualization / Notes / Mappings) was open. Persist the active view in a 'view' URL param and read it back server-side: - give the concept tabs url_parameter: 'view' so clicking a tab records it; - drive each tab's selected: from selected_concept_view? (defaults to details) instead of hard-coding Details; - carry the current 'view' param into the concept_show frame request on class select, via a document-level turbo:before-fetch-request hook (race-free vs rewriting the href; registered once, stateless and idempotent). current_concept_view validates the param against the known view ids (CONCEPT_VIEWS) and falls back to details when it is missing OR unrecognised, so a stale/typo'd ?view=... doesn't leave every tab deselected and the pane blank. TabsContainerComponent gains an opt-in merge_url_params flag rather than changing URL behaviour globally: - merge (concept views): keep the other query params, notably conceptid, when the view changes; - replace (default; top-level p sections, submission section tabs): switching a tab still yields a clean single-param URL with no stale leftovers. The flag is emitted as an explicit "true"/"false" string because Rails renders a boolean data value as a valueless attribute, which the controller would misread. Bonus: the view is now deep-linkable (?view=concept-mappings opens Mappings). Adds OntologiesHelperTest covering the default / valid / bogus / blank param cases and asserting CONCEPT_VIEWS stays in sync with the tab ids in _show.html.haml. Verified in-browser: Visualization/Notes/Mappings survive a class change (URL keeps p+conceptid+view); default and bogus view fall back to Details; deep links open the right view; top-level p tabs produce a clean ?p=... with no stale params. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ontology_object_tabs_component is shared with ontology_object_details_component (schemes, collections), so hardcoding url_parameter/merge_url_params made those pages write ?view=Details on a Details click. Make them arguments defaulting to no URL change, and opt in from concepts/_show.html.haml. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
@matthewhorridge - thanks for this - I reviewed and pushed one commit to the branch.
Hardcoding The commit makes I checked by hand that Details on |
|
Many thanks @jvendetti |
Summary
On an ontology's Classes page, selecting a different class in the left-hand tree
reset the right-hand view back to Details, even when another view
(Visualization / Notes / Mappings) was open. This preserves the active view across
class selections. Fixes #533.
Root cause
Selecting a class reloads the
concept_showTurbo frame, andconcepts/_show.html.hamlhard-codedselected: trueon the Details tab — soevery reload rebuilt the pane with Details active, regardless of the previously
open view. The active view wasn't tracked anywhere.
What changed
Persist the active view in a
viewURL param and read it back server-side:url_parameter: 'view'so selecting a view records it.selected:fromselected_concept_view?(defaults todetails) instead of hard-coding Details.viewparam into theconcept_showframe request on classselect, via a document-level
turbo:before-fetch-requesthook (race-freecompared with rewriting the link href; registered once, stateless, idempotent).
Guarding against a blank pane
Each tab decides on its own whether it's active — its
selected:isselected_concept_view?('<its id>'), i.e. "is the current view my id?". Nothingguarantees that some tab matches. So if the
viewparam is a value no tab has(a typo, a stale link from before a view was renamed, a hand-edited URL), then
every tab's check returns false, no tab is marked active, and the right-hand pane
renders completely blank — no active tab, no content.
To prevent that,
current_concept_viewdoesn't return the raw param. It validatesit against the known view ids (
CONCEPT_VIEWS) and returnsdetailswhenever theparam is missing or not one of them. So an unrecognised
?view=…degrades tothe Details view instead of a blank pane, and only a genuinely valid value selects
a non-default view.
TabsContainerComponentgains an opt-inmerge_url_paramsflag rather thanchanging URL behaviour globally:
conceptid—when the view changes.
psections, submissionsectiontabs):switching a tab still yields a clean single-param URL with no stale leftovers.
The flag is emitted as an explicit
"true"/"false"string because Rails rendersa boolean data value as a valueless attribute, which the controller would
otherwise misread.
The view is now deep-linkable — e.g.
?view=concept-mappingsopens Mappingsdirectly.
Tests
Adds
test/helpers/ontologies_helper_test.rbcovering the default / valid / bogus/ blank param cases, and asserting
CONCEPT_VIEWSstays in sync with the tab idsrendered by
concepts/_show.html.haml(so adding/renaming a view can't silentlyship an unselectable tab).
Verification
Verified manually in a browser:
p+conceptid+view).?view=both fall back to Details.psection tabs still produce a clean?p=…with no stale params.Coverage caveat / follow-up
There's no automated browser/system test for the Turbo + Stimulus round-trip
(the literal issue behaviour). The test suite doesn't currently run in the dev
environment — it's missing the test config (
config/bioportal_config_test.rb), sobin/rails testwon't start, and there's no Capybara system-test setup. Ratherthan add a browser test that can't be run and confirmed here, the gap is logged as
a deliberate follow-up: add a "select Mappings → click another class → Mappings
stays active" system test once the test environment is runnable.
Upstream
The same bug is present in
ontoportal/ontoportal_web_ui(also tracked atontoportal#75). This patch doesn't port cleanly — upstream's
concept tabs have no
id:, include an extrainstancestab, and build theirjson_links differently — so an adapted upstream PR is a separate follow-uprather than part of this one.
🤖 Generated with Claude Code