Skip to content

Implement updates tracking OWID grapher#6

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

Implement updates tracking OWID grapher#6
xrendan wants to merge 1398 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

mlbrgl and others added 29 commits May 21, 2026 11:16
Wrap the narrative chart figure in `.owid-chart-frame` (mirroring
`Chart.tsx`) so the existing `.owid-chart-frame .GrapherComponent
{ display: block; margin: 0 auto }` rule centers the live grapher
post-hydration. Without the wrapper, `.GrapherComponent` kept its
default `inline-block` and anchored at the left edge of the column.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The owid-chart-frame wrapper defaults to a fixed grapher-height, which
assumes the static thumbnail will be replaced by a live Grapher. On
archive pages that hydration never happens, so in narrow column layouts
the wrapper leaves visible blank space below the image. Drop the forced
height when isOnArchivalPage so the image's natural aspect ratio
dictates the frame height; normal pages keep the fixed height to avoid
post-hydration CLS.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ators

🐛 fix external indicators in the entity selector
🔨 (analytics) add viewConfigId to more events
🐛 center narrative chart in articles
Extends the Algolia pages-chronological index to support richer cards on /latest:
adds kicker faceting (content type), custom thumbnails, excerpts, linked-image
metadata, and announcement attachments. Reuses gdoc.linkedImageFilenames for
consistent image resolution and adds admin validation for latest-featured-image.
Replaces the server-rendered /latest page with a lightweight shell that mounts
the new Algolia-powered SPA search on the client. Removes the page-specific SSR
data loading, simplifying the baker and site renderer paths.
Add Algolia search query helpers, Latest page filter encoding/decoding,
custom hooks for search state management, and shared layout constants.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add the main search interface with InstantSearch integration, topic
facet pills with counts, and the wrapper that initializes Algolia.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Also fix author avatar alignment when some authors lack profile images.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add LatestHit component that routes to per-type hit cards. Register all
new component SCSS in owid.scss and remove old LatestPage card styles.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Match the convention used elsewhere in site search (SearchDataResults,
SearchWritingResults): on initial fetch render placeholder cards that
mirror the LatestHit layout instead of a centered "Loading…" string.
Reuses the existing .animate-pulse keyframes from search/skeletons.scss.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collapses the (type, kicker) pair on PagesChronological records into
one flat latestType attribute with five values (article, data-insight,
data-update, website-upgrade, announcement). The raw OwidGdocType stays
on the record so the dynamic atom feed keeps filtering on type as before.

- Adds LatestType + LATEST_TYPE_VALUES in the shared types package.
- Derives latestType at index time; logs unrecognized announcement
  kickers via logErrorAndMaybeCaptureInSentry with a safe fallback to
  "announcement".
- Swaps kicker for latestType in attributesForFaceting.
- Simplifies queryLatestPages: filters is always the static base
  filter; user-selected type and topics each live in their own
  facetFilters group, dropped per-query to drive disabling counts.
- Drops LatestFilter tagged union, encodeFilter, the kicker: URL
  prefix, and the dual TYPE_META/KICKER_META lookup in favor of a
  single LATEST_TYPE_META map keyed by latestType.

URL changes from ?type=kicker:data-update to ?type=data-update.
Requires a PagesChronological reindex on deploy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Header & mobile nav: rename "Insights" → "Latest", point at /latest
- Homepage hero: new "Articles" pill linking to /latest?type=article,
  backed by a server-side article count
- "See all Data Insights" homepage button: → /latest?type=data-insight
- Footer "Insights": → /latest?type=data-insight (kept alongside "Latest")
- Featured Data Insights, small-screen carousel, and DI permalink page
  "see all" + breadcrumb: → /latest?type=data-insight
- Homepage DI carousel cards: link to per-DI permalink instead of
  scroll-anchoring on the soon-to-be-deleted /data-insights index
- Centralize /latest URL construction in latestUrl() helper
  (renamed latestFilters.ts → latestUrl.ts)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The paginated /data-insights index is now redundant — every entry point
that used to land there now lands on /latest?type=data-insight (commit
237a063). Delete the page, its baking pipeline, sitemap entries, dev
preview branch, and orphaned constants/helpers. Add a permanent 301
redirect (bare + wildcard) so external links and search engines land in
the right place. Individual /data-insights/{slug} permalinks, the atom
feed, and the entire admin UI are unaffected. Existing slug-rename
redirects in the DB still take precedence over the new wildcard.

Also redirect /latest/page/N (retired SSR pagination, no SPA analog) and
collapse /blog/* onto /latest — the existing /blog/* /latest/:splat rule
was 404-ing for every input, since /latest has never had a /:slug route.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adopt the searchParamsToState / stateToSearchParams / urlNeedsSanitization
pattern from site/search/searchState.ts. Unknown params (e.g. legacy
?topic=Health from old /data-insights links), invalid topic names, and
unrecognized type values are now stripped from the URL on first paint
via a replaceState. Filter mutations build URLs from validated state
instead of copying URLSearchParams from the previous URL, so unknown
params can't sneak back in.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Introduce LatestFeedGdoc (subset of ChronologicalGdoc shown on /latest)
  derived structurally from LATEST_FEED_TYPE_VALUES, plus a
  checkIsLatestFeedGdoc predicate. Tighten ChronologicalGdoc so
  content.type is non-optional, eliminating one ! assertion in the indexer.
- Split deriveLatestType into a dispatcher that takes LatestFeedGdoc and
  a deriveAnnouncementLatestType(kicker) helper. The two announcement-only
  call sites (standalone preview, homepage card) call the helper directly,
  so the dispatcher is only used by the indexer (gated behind the new
  predicate). Dispatcher uses ts-pattern shape-match — no `as` casts.
- Subset enforcement: LATEST_FEED_TYPE_VALUES `satisfies` the chronological
  tuple at compile time. LATEST_BASE_FILTER (Algolia query) and
  LATEST_FEED_TYPES (Set) both derive from it, single source of truth.
- Narrow getHomepageAnnouncements return type to
  OwidGdocMinimalAnnouncementInterface (the SQL already filters
  type='announcement'); propagate through OwidGdocHomepageMetadata and
  HomepageIntro.
- Rename checkIsChronologicalFeedPost → checkIsChronologicalGdoc and
  checkIsLatestFeedPost → checkIsLatestFeedGdoc to match the type names
  and the codebase's checkIsX convention.
- Rename typeFacetFilter → latestTypeFacetFilter in queries.ts (it filters
  on the latestType attribute, not type — LATEST_BASE_FILTER is the type
  filter).
- Fix three misleading comments in pagesChronological.ts: the topic-page
  branches and isChronologicalGdocInstance gate were claimed to be
  unreachable / skipping, but topic pages are in fact chronologically
  indexed (for the atom feed) and reach those branches at runtime — the
  noop is the actual behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds site/latest/README.md describing the architectural patterns of the
new /latest SPA: records carrying their own AttachmentsContext, the two
indexing paths (bulk + individual), URL as filter-state source of truth,
the batched facet-counting search, the latestType derived field, and the
preview-surface role of standalone announcement pages. Mirrors the shape
of site/search/README.md and notes the deliberate parallel between the
two surfaces.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror the /search analytics pattern on /latest. Two new GA4 events are
added to the registry: SiteLatest fires on filter state change, and
SiteLatestResultClick fires when a card link is clicked. A new
useLatestAnalytics hook debounces the filter event (skipping default
state and consecutive duplicates), and a LatestContext exposes the
SiteAnalytics instance to hit components.

Announcement cards intentionally don't track clicks — they have no
canonical link to their own slug, mirroring how links inside the rich
excerpt of an article hit are also untracked.

LatestState moves into @ourworldindata/types alongside SearchState.

GTM registration of owid.site_latest, owid.site_latest_result_click,
latestTopics, and latestType is required before GA4 will collect the
new params.
Drop the redundant "Site" prefix to match the dominant naming pattern
in SiteAnalytics (logSearch, logInstantSearchClick,
logSearchAutocompleteClick, and the new logLatestResultClick). The
EventCategory.SiteSearchResultClick GA4 event name is unchanged — the
"Site" prefix there is a deliberate namespace.
Marigold and others added 30 commits June 5, 2026 10:31
Extract dropdown components useful in bespoke projects
* ✨🤖 make datapages view mergeable to speed up per-chart lookups

MySQL cannot merge views that contain window functions or derived
tables, so `SELECT variableId FROM datapages WHERE chartId = ?`
materialized the whole view — a scan over all of chart_dimensions,
three times — on every call. The baker runs this query once per chart
(~2.6k times in the SiteBaker prefetch and ~4.5k times when baking
grapher pages), which added roughly 15 minutes to staging bakes.

Recreate the view with correlated subqueries instead of CTEs so it
stays mergeable (ALGORITHM=MERGE) and the chartId predicate is pushed
down, turning each lookup into a few indexed reads (~44ms -> ~0ms).

Verified row-by-row against the previous definition on a live DB:
identical result set (3109 rows, 0 differences in either direction).

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

* 🤖 style: format code

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Marigold <1550888+Marigold@users.noreply.github.com>
Tests in the /bespoke folder used to run with `yarn test`. They're now separated into their own testing suite, and a new github action for running bespoke tests is added.
🔨 (bespoke) extract shared Spinner component
- Update custom icon sizes and viewBoxes to better match Font Awesome
- Add padding to text labels to improve their vertical alignment
* ✨🤖 Add "informality" synonym group

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

* 📜 Clarify add-synonym skill: new array per section is allowed

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

* 📜 add-synonym skill: require a proper PR description

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

---------

Co-authored-by: Claude <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.