Skip to content

Implement updates tracking OWID grapher#6

Open
xrendan wants to merge 1574 commits into
BuildCanada:masterfrom
owid:master
Open

Implement updates tracking OWID grapher#6
xrendan wants to merge 1574 commits into
BuildCanada:masterfrom
owid:master

Conversation

@xrendan

@xrendan xrendan commented Feb 26, 2026

Copy link
Copy Markdown
Member

Context

Links to issues, Figma, Slack, and a technical introduction to the work.

Screenshots / Videos / Diagrams

Add if relevant, i.e. might not be necessary when there are no UI changes.

Testing guidance

Step-by-step instructions on how to test this change

  • Does the change work in the archive?
  • Does the staging experience have sign-off from product stakeholders?

Reminder to annotate the PR diff with design notes, alternatives you considered, and any other helpful context.

Checklist

(delete all that do not apply)

Before merging

  • Google Analytics events were adapted to fit the changes in this PR
  • Changes to CSS/HTML were checked on Desktop and Mobile Safari at all three breakpoints
  • Changes to HTML were checked for accessibility concerns

If DB migrations exists:

  • If columns have been added/deleted, all necessary views were recreated
  • The DB type definitions have been updated
  • The DB types in the ETL have been updated
  • If tables/views were added/removed, the Datasette export has been updated to take this into account
  • Update the documentation in db/docs

After merging

  • If a table was touched that is synced to R2, the sync script to update R2 has been run

[Cycle issue](#6169) / [Designs](https://www.figma.com/design/dCB69o19cAVtuk283STAcY/Dumbbell-Plots?node-id=1-31&m=dev)

Implements hover, focus and tooltips for dumbbell plots.

### Description

* Focus mode is only available to authors, i.e. via the admin or by editing the URL params directly
    * Hovering and updating the selection clears focus (this is how it works for other chart types as well)
* The hovered dumbbell is highlighted by muting all other dumbbells
    * I wasn’t sure whether it’s better to use the entire row, including the label and whitespace on the left and right, as the hover hit area, or only the actual dumbbell. I opted for the former because it makes hovering a bit easier I think
* The two modes, entity and column series strategy, have different tooltips:
    * If series strategy = entity, the tooltip is similar to the slope tooltip (start value -> end value)
    * If series strategy = column, the tooltip is similar to the scatter tooltip (start and end value on separate rows)
        * The change value is also shown if it’s used to label the dumbbell

### Follow-up work

* ~Dumbbell elements~
* ~New dumbbell-specific options: value labels, end points~
* ~In-chart legend, aligned with the top-most dots~
* ~Interaction (focus, hover, tooltips)~
* Color (hard-coded for now)
* More entity sorting options
* Dumbbell plots in search
* Dumbbell chart thumbnails
Small refactor of the props passed to tooltip components
Adds support for dumbbell charts in search.

This includes:
- Constructing the data table rendered in search
- Making sure the big data value on the right is appropriate
    - Should say `Start value -> End value` in time-range mode (same as slope charts)
    - Otherwise, it should be hidden (we never show a value for multi-y charts)

The thumbnail PNGs (with imMinimal=0/1) look good as is, I think.

This is a bit difficult to test. I mainly looked at the various endpoints (`.search-result.json`, `values.json`) and added tests.

This PR also includes a refactor that drops the dumbbell's series strategy and replaces it with a custom `mode`. If the series strategy is set to `column`, search expects the series items to refer to columns, not entities. But dumbbell charts always plot entities, so the appropriate series strategy is `entity`, and we should differentiate between time-range and two-column mode separately.

### Follow-up work

* ~Dumbbell elements~
* ~New dumbbell-specific options: value labels, end points~
* ~In-chart legend, aligned with the top-most dots~
* ~Interaction (focus, hover, tooltips)~
* Color (hard-coded for now)
* More entity sorting options
* ~Dumbbell plots in search~ (addressed in this PR)
* ~Dumbbell chart thumbnails~ (addressed in this PR)
Adds three more sorting options for dumbbell plots: by start and end value, and by change.

Also refactors the sorting code a bit: Chart types now expose `availableSortKeys`, which is then used in the admin to construct dropdown options, and to narrow the type for the object specifying the sorting options.

Marimekkos are excluded from the refactor because their sorting is intermingled with other code, and sooner or later we'll rewrite the Marimekko chart component anyway.

### Follow-up work

* ~Dumbbell elements~
* ~New dumbbell-specific options: value labels, end points~
* ~In-chart legend, aligned with the top-most dots~
* ~Interaction (focus, hover, tooltips)~
* Color (hard-coded for now)
* ~More entity sorting options~
* ~Dumbbell plots in search~
* ~Dumbbell chart thumbnails~
Allows color customisation for dumbbell charts.

* For two-column mode: Authors can assign an indicator colour via the admin
* For time-range mode: Authors can assign colours in the dumbbell section via the admin. These are mapped to the new `dumbbell.trendColorMap` config field.
    * Instead of introducing a dumbbell-specific colour mapping, we could also re-use the top-level colorMap config field. But the `colorMap` is shared between all chart types, so if the dumbbell chart is shown next to a line chart, the line chart colour scheme would also apply to the dumbbell chart, which isn’t ideal
    * Instead of introducing a new config field type, we could also re-use the existing colorMap type inside the dumbbell namespace. This feels like overkill though, and it’s also not a great fit because the internal ColorScale needs a color column, which doesn’t exist for dumbbells since there’s no column encoding increase/decrease/noChange. Of course, we could create one on the fly, but that seemed unnecessarily complex
    * I also considered simply not allowing customisation, but at the very least, we’d need an “invert colors” config because mapping increase to green and decrease to red doesn’t work when “decrease is good”. So if we have to add one piece of config anyway, we might as well allow customisation
    * I also considered not allowing customisation and simply choosing neutral colours for increase/decrease, but Marwa wasn’t a fan
Misc touchups for dumbbell charts after Marwa's feedback
Surface the existing type=Dumbbell embed test view on the admin test
index page, alongside the other chart-type links.
sophiamersmann and others added 30 commits July 2, 2026 11:42
Add click-to-select behaviour to Sankey nodes, adapted from #6613
Adds a population share tooltip for the migration bespoke viz, adapted from #6613
The "download displayed data" button on the data table tab exported the
full timeline instead of the currently selected time range, unlike the
chart/map tabs which already filter via transformedTable.

Filter tableForDisplay by [startTime, endTime] in filteredTableForDownload
when on the table tab. This fixes both the client-side and server-side
download paths, since both read the same getter.

Fixes #6602

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* 🐝 🤖 Clean up tsconfig base options

Switch `module` from `commonjs` to `preserve` so it matches our
`bundler` module resolution and the ESM reality of our toolchain — the
emitted JS is never run (tsx and Vite consume the TS source directly),
so the previous CommonJS setting was both inert and inconsistent.

Drop options that are redundant with the inherited `@tsconfig/node24`
base or with current TS defaults:
- `sourceMap` — emitted .js.map files are never used; nothing runs or
  debugs the tsc output.
- `alwaysStrict` — implied by the base's `strict: true`.
- `forceConsistentCasingInFileNames`, `allowJs` — already the defaults.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Remove explorerJobsWorker examples using bare Node

We only deploy it using tsx and supporting Node is not necessary.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Fix styling: it rendered unstyled at the bottom of the page because
  the SCSS is scoped to .GrapherComponent. Since we use it only on
  standalone Grapher pages, it can render inside Grapher instead.
- Convert it to a plain function component.
- Replace imperative toggle with React state.
- Ensure all commands run in a MobX action
🐛 Restrict table-tab data download to the selected time range
Signed-off-by: dependabot[bot] <support@github.com>
Replace the entity dropdown's grouped Selected/Suggested/All layout with a
single ordered list: any pinned aggregates first (e.g. 'All countries'), then
the selected entity, then the user's country and continent, then the remaining
entities alphabetically. The user's country and continent are marked with a
location icon wherever they appear, including when one of them is selected.
Rename the whole-slideshow delete button/confirm to say
'Delete slideshow' and the single-slide delete action to say
'Delete slide' (with a tooltip/aria-label since it's icon-only),
so the two destructive actions are unambiguous at the point of
the click. Also strengthen the slideshow delete confirmation copy
to make clear the whole deck is being permanently deleted.
Makes the destructive whole-slideshow delete action visually more
distinct from the lightweight single-slide delete confirmation, in
both the editor footer and the slideshows index page.
Authenticate staging admin via Tailscale Serve headers
Fix staging Serve admin auth through nginx
* 🎉🤖 Add optional license field to chart configs

Charts default to CC BY, but a stricter Creative Commons license (e.g.
CC BY-NC-ND) can now be set per chart when a data provider requires it.

- Add optional "license" enum (all six CC licenses, default cc-by) to
  the grapher schema (non-breaking) and GrapherInterface, with a
  CHART_LICENSES name/URL map in @ourworldindata/types
- Chart footer shows the configured license and links to its
  creativecommons.org page; the footer hover tooltip drops the
  "free to use, share, and adapt" wording for non-CC-BY licenses
- Add a License dropdown to the chart editor's Text tab
- Data pages: "Reuse this work" renders a provider-specific notice via
  a shared ChartLicenseNotice component (both old and new layouts)
- JSON-LD on grapher and data pages emits the actual license URL

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

* 🐛🤖 Address Codex review: show license without OWID logo, plumb into multi-dim pages

- A configured non-default license now overrides the "Powered by
  ourworldindata.org" footer fallback on charts without the OWID logo,
  since provider-required license terms must always be shown
- Expose license on DataPageDataV2 (from the view's grapher config) and
  pass it to MetadataSection on multi-dim data pages

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
✨ Order the bespoke entity dropdown by user location
Per mille (‰) is not widely understood, so display the net migration
rate as a percentage instead. This is display-only: the stored data and
simulation model stay in per-1,000 units, and values are converted
(divide by 10) only at the axis, value editor, and formatted labels via
a new `displayScale` config field. Display precision is kept separate
from the stored per-1,000 precision via `displayDecimals`, so URL
serialization and modified-vs-baseline detection are unaffected.
✨ Show net migration rate as % instead of ‰
Staging servers built with the `staging-bake` label run the deploy queue
against a Buildkite pipeline (grapher/etl-automated-staging-environment),
triggering a build for their own branch via the REST API. Those pipelines
are PR-triggered ("Build branches" is off), so an API build for a branch
is rejected with 422 "Branches have been disabled for this pipeline", which
the deploy queue then retries indefinitely (Sentry ADMIN-N4, 17k+ events).

- Send `ignore_pipeline_branch_filters: true` so the deliberate
  API-triggered branch build is accepted regardless of the pipeline's
  branch trigger/filter settings. Verified against the live pipeline: the
  same payload returns 422 without the flag and 201 with it.
- Include the pipeline slug, branch, and an explanation in the thrown
  error so this is self-diagnosing next time instead of a bare `Error: 422`.

Co-authored-by: Claude Opus 4.8 (1M context) <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.