feat(website): add version diff table on "All versions" page#6726
feat(website): add version diff table on "All versions" page#6726corneliusroemer wants to merge 19 commits into
Conversation
|
This PR may be related to: #2803 (Diff view of Loculus versions) |
There was a problem hiding this comment.
Pull request overview
Adds an interactive “version diff” view to the sequence All versions page (/seq/[ACCESSION]/versions) so users can quickly see what metadata/sequence-derived fields changed between two released versions, with shareable URL state.
Changes:
- Replaces the server-rendered versions list with a React
VersionsWithDiffcomponent that supports selecting two versions and toggling diff display options. - Introduces diffing/rendering logic for
details.jsontable fields (including mutation-specific diffing and section ordering aligned to the details page). - Extends Playwright integration coverage to validate the diff UI behavior across 2-version and 3-version accessions.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| website/src/pages/seq/[accessionVersion]/versions.astro | Swaps the static version list for the new React diff-enabled versions view. |
| website/src/components/VersionDiff/VersionsWithDiff.tsx | Main UI: version history list, selection controls, and diff table rendering with react-query. |
| website/src/components/VersionDiff/useVersionComparison.ts | Owns version pair selection, URL persistence (?compare=), and fetching/comparing details.json. |
| website/src/components/VersionDiff/DiffTable.tsx | Renders grouped diff table with toggles for unchanged fields and shared mutation hiding. |
| website/src/components/VersionDiff/DiffFieldValue.tsx | Renders field values in diff cells, reusing details-page renderers where appropriate. |
| website/src/components/VersionDiff/compareVersions.ts | Compares two details.json payloads into changed/unchanged/noisy field groups. |
| website/src/components/VersionDiff/mutationDiff.ts | Computes mutation/indel-only differences for mutation fields (diff-only mode). |
| website/src/components/VersionDiff/mutationDiff.spec.ts | Unit tests for mutation/indel diff logic. |
| website/src/components/VersionDiff/types.ts | Types for comparison results and per-field comparisons. |
| website/src/components/SequenceDetailsPage/groupTableDataByHeader.ts | New shared helper to group/order fields consistently with the details page layout. |
| website/src/components/SequenceDetailsPage/getDataTableData.ts | Refactors table grouping to use the shared groupTableDataByHeader helper. |
| website/src/components/SequenceDetailsPage/DataTableEntryValue.tsx | Exports FileListComponent for reuse in the diff view renderer. |
| website/src/components/SequenceDetailsPage/MutationBadge.tsx | Avoids rendering empty segment headers for mutation containers. |
| website/src/components/common/withQueryProvider.tsx | Fixes QueryClient lifecycle by creating it once per mounted provider (stable cache). |
| integration-tests/tests/specs/features/sequence-version-banners.spec.ts | Extends integration test to cover diff functionality and a third revision with sequence changes. |
| integration-tests/tests/pages/versions.page.ts | Adds a page object for interacting with and asserting the versions + diff UI. |
| integration-tests/tests/pages/sequence-detail.page.ts | Returns the new VersionsPage object from gotoAllVersions() and removes inline assertions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Add interactive diff view to compare metadata changes between sequence versions.
Features:
- Smart defaults: auto-compares when only 2 versions exist
- Checkbox selection for 3+ versions
- URL persistence with ?compare=1,3 for shareable links
- Toggle to show all fields vs only changed fields
- Noisy fields (timestamps, version info) displayed greyed out
- Fields grouped by metadata headers
- Visual highlighting of changed values with amber background
Components:
- VersionsWithDiff: Main React component with version selection
- DiffTable: Table display component for field comparisons
- compareVersions: Logic to identify changed/unchanged/noisy fields
- Integrated into existing /seq/{accession}/versions page
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…syUI Replace DaisyUI classes (checkbox, toggle, loading-spinner, alert-error) with project-standard components (Checkbox, Spinner, ErrorBox) and plain Tailwind. Also use Checkbox component which handles hydration internally, eliminating the need for manual useClientFlag wiring. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Refactor the version diff view to fetch data the way the rest of seqdetails does, and fix a couple of UX issues found along the way. - Extract selection state, URL persistence (?compare=1,2) and data fetching out of VersionsWithDiff into a new useVersionComparison hook, replacing the two ad-hoc useEffects with a single react-query useQuery. - Validate fetched details.json at runtime with detailsJsonSchema instead of casting. - Keep the previous comparison rendered while a new pair loads (keepPreviousData) and dim it with an overlay instead of unmounting the table, fixing the page scroll jumping when toggling versions. - Render the island with client:only since it relies on browser APIs (window.location) that aren't available during SSR. - Fix withQueryProvider creating a new QueryClient (and empty cache) on every render by constructing it lazily once via useState. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ze diff table layout
Extend the existing 'can navigate to versions page' integration test (which
already produces two released versions differing in collection date) to also
assert the version diff view:
- auto-compares the two versions ("Comparing Version 1 vs Version 2")
- shows the changed Collection date field with both old and new values
- persists the ?compare=1,2 URL param
- "Show all fields" toggle reveals an unchanged field (Collection country)
Add a dedicated VersionsPage page object for the /seq/{accession}/versions
page (version list assertions + diff-view interactions), and have
SequenceDetailPage.gotoAllVersions() return it, since it is a distinct page.
Also stabilize the DiffTable layout: switch to table-fixed with a fixed-width
field column so the column widths no longer jump when toggling "Show all
fields", and wrap long values.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reuse the sequence details page's header grouping/ordering in the version diff table so fields and header groups appear in the central, config-defined order (orderOnDetailsPage) instead of arbitrary insertion order. - Extract groupTableDataByHeader() shared helper from getDataTableData - Carry orderOnDetailsPage through FieldComparison - Use the shared helper in DiffTable Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When no ?compare param is present, the diff view now defaults to comparing the two most recent versions (e.g. 2 & 3 when three versions exist) instead of only auto-comparing when exactly two versions exist. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The displayGroup branch created the group's array only when it already existed, so the first entry of a group dereferenced an undefined array and threw "Cannot read properties of undefined (reading 'push')". Create the array when the group is not yet present. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rsion diff Drop hidden fields (data use terms) from the version diff entirely, filtered once up front in compareVersionData so neither comparison pass considers them. Order diff sections like the sequence details page: general sections first, then alignment/QC, then mutation details. The details page forces alignment/QC to the bottom via DataTable section partitioning rather than mean orderOnDetailsPage, so add a shared headerSectionRank helper and apply it as a stable sort in DiffTable. Also rename the getDataTableData displayGroup helper to groupCustomDisplayEntries. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extend the versions-page integration test with a third revision that changes the sequence (one SNP + one in-frame deletion), giving three released versions. On the versions page this exercises: - the version-selection checkboxes shown for 3+ versions - the default comparison of the two most recent versions (sequence-derived Length change shown, unchanged Collection date hidden) - flipping the selection to compare versions 1 and 2 (Collection date diff, ?compare=1,2) Add VersionsPage helpers (expectVersionSelectionAvailable, toggleVersionSelection, expectFieldRowPresent) and drop the superfluous "(selected: x/2)" counter text. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mutation/insertion/deletion fields were never shown in the version diff because they carry an empty `value` and store their content in `customDisplay`. The diff now carries the full table entries for each version and renders cells with a curated, lightweight custom-display renderer (DiffFieldValue): - mutations/indels (badge, list, generatedBadge) render as the same mutation badges used on the sequence details page - links (incl. INSDC) and file lists keep their custom views - everything else (grouped/composite, constant, or context-dependent displays like geoLocation, submittingGroup, variantReference, dataUseTerms) falls back to a plain text representation Comparison uses a stable key per field (flattened mutation string for mutation fields, the raw value otherwise) so changes are detected correctly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mutation/insertion/deletion cells now show only the mutations that differ between the two compared versions: each column lists just its removals/additions, with mutations shared by both versions dropped. This keeps the diff readable even when a sequence has hundreds of mutations; the full list remains available on each version's sequence page. The set difference is computed per segment from the structured customDisplay data (badge / list). Mutation section headers carry a caveat noting that only differing mutations are shown. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Version diff now shows the full mutation lists for both versions by default, with a single "Only show differing mutations" checkbox that switches all mutation-like fields (substitutions, deletions, insertions, nucleotide and amino acid) to a diff-only view at once. The mutation diff is computed at render time from the full entries (rather than precomputed during comparison), so the toggle is purely a view concern. The "differences only" caveat on mutation section headers is shown only while the toggle is active. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Label the mutation diff toggle "Hide shared substitutions/indels" instead of "Only show differing mutations". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…parator Move the "Hide shared substitutions/indels" checkbox from the top of the version diff into the first mutation section header, so it sits next to the fields it affects instead of requiring a scroll. Also render mutation badges/strings in the diff without the per-segment heading separator: that heading renders as a bare horizontal line for the common single, unnamed segment and looked out of place in the diff table. A light segment label is now shown only when there is an actual segment name (multi-segment organisms). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When hiding shared substitutions/indels, a mutation field that has no version-specific entries now renders blank rather than "None". "None" would wrongly suggest the version has no mutations at all, when it really means nothing differs. The normal full view still shows "None". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ent name The per-segment heading in the mutation badge/string containers rendered as a bare horizontal separator line for the common single, unnamed segment. Render the heading only when there is an actual segment/gene name, fixing both the sequence details page and the version diff. With the separator fixed at the source, the version diff drops its bespoke segment renderers and reuses SubstitutionsContainers / MutationStringContainers directly again. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
720d146 to
1fffcfa
Compare
There was a problem hiding this comment.
I wonder if this page should be called Compare Versions instead of All Versions - I am not sure I would have found it as a normal user
| <li key={version.version} className='mb-4'> | ||
| <div className='flex items-start gap-3'> | ||
| {showCheckboxes && ( | ||
| <Checkbox |
There was a problem hiding this comment.
I think eventually we should change this to two columns of radio buttons - can make an issue some time - but this is fine for now
There was a problem hiding this comment.
(wikipedia is a good example)
| 'CAATATCCAACTTCCTGGCAATCAGTTGGACACATGATGGTGATCTTCCGTTTGATGAGAACAAACTTTTTAATCAAGTTCCT' + | ||
| 'ACTAATACATCAGGGGATGCACATGG'; | ||
|
|
||
| // TEST_SEQUENCE with one point mutation (SNP) and one in-frame (3-nt) deletion, used to |
There was a problem hiding this comment.
The tests do not appear to check that these sequence mutation differences are shown on the comparison page?
| } | ||
|
|
||
| /** Returns the segments/strings of `a` that are not present (per segment) in `b`. */ | ||
| function subtractSegmentedStrings( |
There was a problem hiding this comment.
these are insertions and deletions right? maybe we should add to doc string
anna-parker
left a comment
There was a problem hiding this comment.
looks great! I revised metadata and sequences across all organisms (includings EVs and multi-seg, multi-ref) and this works great!
Add version diff table to
/seq/[ACCESSION]/versionsto allow quick identification of what changed between versions.Preview ready for testing (until it gets wiped): https://diff-versions-v2.loculus.org/seq/LOC_0002U7N.3/versions?compare=2%2C3
Based on #5760
Resolves #2803
Key features:
Caveat: There are edge cases where a sequence can change and this is not visible in this view as
/details.jsondoes not include all information required to fully reconstruct a sequence. E.g. the position of Ns could change and this would not necessarily be visible (unless it causes differences in substitutions/indels). This could be worked around by fetching sequences separately from/details.jsonbut I decided to keep it simple for nowAdditional changes
withQueryProviderfunction to not recreate query client on each React render through lazy initializationTests
I extended existing integration tests that revise sequences to now also check basic functionality of the diff component.
Screenshot/cast
Screen.Recording.2026-06-22.at.16.29.24.mp4
🚀 Preview: https://diff-versions-v2.loculus.org