Skip to content

fix: draw analytics charts in inter and fetch their data without waiting for chart.js (#5033) - #5035

Open
frano-m wants to merge 3 commits into
mainfrom
frano-m/5033-analytics-chart-startup
Open

frano-m wants to merge 3 commits into
mainfrom
frano-m/5033-analytics-chart-startup

Conversation

@frano-m

@frano-m frano-m commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Closes #5033

What changed

In the static analytics template (analytics/analytics_package/analytics/static_site/template/index.html):

  • Data fetches start right away. loadData() now runs as soon as the inline script does, instead of on DOMContentLoaded. The stat cards, tables and event stats render as soon as their data arrives.
  • Charts wait for Chart.js and their data. A chartJsReady promise resolves on DOMContentLoaded once Chart.js has run, and loadData resolves a chartDataReady promise with the charts' data. Chart drawing (traffic, pageviews, event trend charts) is one chain on both, attached at startup and kept out of loadData's try. If Chart.js fails to load, the promise rejects, only the charts are missing, and the console shows one "Chart.js failed to load" error, whichever of Chart.js and the data settles first.
  • Event cards and their charts are separate. renderEventCounts now renders only the card HTML. The new eventChartDataByKey builds the lookup, and the new renderEventTrendCharts draws the trend charts once Chart.js is ready.
  • Charts end up in Inter.
    • When Chart.js is ready, if Inter is already loaded (the Inter <link>'s media is all and document.fonts.check('400 12px Inter') is true), the charts start in --fui-font-family.
    • Otherwise they draw in the fallback stack (FALLBACK_FONT_FAMILY, which is --fui-font-family without Inter). Once the stylesheet's load fires and document.fonts.load('400 12px Inter') resolves, useInterInCharts sets Chart.defaults.font.family to --fui-font-family and calls update('none') on every chart.
  • The Inter <link> itself is unchanged (onload="this.media='all'").

The generated pages gh-pages/{anvil-explorer,hca-explorer,lungmap}/index.html are re-rendered from the updated template, and the analytics package version is now 5.1.4.

Why

#5026 deferred Chart.js and stopped the Inter stylesheet from blocking first paint, which caused three problems:

  • The 12 data/*.json requests waited for the cross-origin Chart.js download.
  • A Chart.js CDN failure left the whole report empty.
  • Nothing waited for Inter before the charts drew. Canvas text isn't redrawn when a font arrives, so labels could stay in the system font.

Assumptions I made

  • Draw first, then redraw in Inter. Charts draw straight away and switch to Inter when it loads, rather than waiting for Inter with a timeout. This matches how the page text swaps fonts (display=swap), and it is the pattern the findable-ui analytics.css notes document (findable-ui#1213).
  • Why the font family changes on the switch. In Chart.js 4.5.1, each scale caches label widths keyed by the font string and keeps them across update(). A plain update() with an unchanged family would reuse the widths measured in the fallback font. So the charts start with a family string that leaves out Inter, and switching to the Inter string makes Chart.js measure the labels again.
  • Why the media check, not link.sheet. "Inter is ready" is detected with the link's media === 'all', which its onload attribute sets. link.sheet isn't used because Chrome sets it even after the stylesheet fails, and media stays print until the stylesheet is actually applied.
  • When Inter never arrives. If Google Fonts fails, the charts stay in the fallback font, the same as the page text, and nothing waits on it. If the stylesheet loads but the font file is blocked, document.fonts.load rejects; that rejection is handled with no further action, because the fallback font is the intended result and the browser already logs the failed request.
  • Only the 400 weight is awaited. Tooltip titles are drawn on hover, by which point the page's headings have loaded the 500 face.
  • Errors aren't caught. A Chart.js load failure, or an exception thrown while drawing a chart, shows in the console as an uncaught promise rejection with its stack trace, rather than being caught and logged. This follows the project's error-handling rule against catching an error only to log it.
  • How the pages were re-rendered. Each site's config literal was read from its generate_static_site.py without running the script, so there was no GA login and no data refresh. The data/ folders are unchanged.
  • No new tests. The repo has no JavaScript tests for the static template. The behaviour was checked by hand in Chrome; see below.

How to verify

Serve a generated page, for example cd gh-pages/hca-explorer && python -m http.server 8080, and open http://localhost:8080 in Chrome with DevTools open and Disable cache on.

  1. Set Network throttling to Slow 4G and reload. Once the page text is in Inter, check that the chart axis labels and legends are in Inter too.
  2. On the same load, in the Network waterfall, check that the data/*.json requests start before the chart.js@4.5.1 request finishes.
  3. In DevTools, block the request URL cdn.jsdelivr.net and reload. Check that the stat cards and tables render, only the charts are missing, and the console shows one "Chart.js failed to load" error.
  4. Unblock it and reload. Hover a chart and a stat card to see their tooltips. Open the Engagement Rate toggletip, then press Escape to close it. Click a "Show all" toggle.
  5. Check that git diff main -- gh-pages shows the same changes as the template diff for all three sites.

Definition of done:

  • With Network throttling on and the cache disabled, chart axis labels, legends and tooltips are drawn in Inter once it has loaded, including when the data JSON arrives before the font. Step 1
  • The Network panel shows the data/*.json requests starting before the Chart.js request finishes. Step 2
  • With cdn.jsdelivr.net blocked, the stat cards and tables still render, and only the charts are missing. Step 3
  • Chart tooltips, stat-card tooltips and toggletips (including Escape to close), and the "Show all" toggles still work. Step 4
  • The generated pages under gh-pages/ are re-rendered from the updated template. Step 5

What I checked in Chrome on local copies of hca-explorer:

  • Fetch order. With Chart.js uncached, all 12 initial data/*.json requests started at 77 ms, and the Chart.js download finished at 131 ms.
  • Inter already cached. All 8 charts drew once, straight in Inter.
  • Slow Inter stylesheet. I delayed the stylesheet 4 s. The charts drew in the fallback font, then switched to Inter, and the axis widths were measured again.
  • Chart.js missing. I pointed the page at a Chart.js version that doesn't exist. The stat cards, tables, event cards and detail tables rendered, with no "Error loading data".
  • Interactions. The tooltips, the toggletip with Escape, and "Show all" worked.

🤖 Generated with Claude Code
🐦 Built with cc-claude-tools

…ing for chart.js (#5033)

- start the loaddata fetches as soon as the inline script runs, and render
  the stat cards and tables as their data arrives
- draw the charts once chart.js has loaded (chartjsready); if it fails to
  load, only the charts are missing
- draw charts in inter straight away when it is already loaded, otherwise
  draw them in the fallback stack and switch chart.defaults.font.family to
  --fui-font-family once the inter stylesheet and face have loaded, so
  chart.js measures the labels again
- split the event trend chart drawing out of rendereventcounts
- re-render gh-pages anvil-explorer, hca-explorer and lungmap with their
  existing data, and bump the analytics package to 5.1.4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cc-claude-tools <cc-claude-tools@clevercanary.com>
@frano-m
frano-m requested a balanced review from Copilot November 13, 2026 07:13
@frano-m
frano-m marked this pull request as ready for review November 13, 2026 07:14
…ile fails to load (#5033)

- handle the document.fonts.load rejection (for example a csp that allows
  fonts.googleapis.com but blocks fonts.gstatic.com) so it isn't an
  unhandled promise rejection; errors from useinterincharts still surface
- re-render gh-pages anvil-explorer, hca-explorer and lungmap

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cc-claude-tools <cc-claude-tools@clevercanary.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The chart/data coordination handler is attached too late, allowing an early Chart.js failure to produce duplicate unhandled rejections in the template and generated pages.

1 open issue
What changed in this PR

Addresses #5033 by separating analytics data loading from Chart.js readiness and redrawing charts in Inter when the font loads, building on the deferred loading introduced in #5026.

Changes:

  • Starts data requests immediately and renders non-chart content independently of Chart.js.
  • Separates event cards from chart drawing and adds fallback-to-Inter font updates.
  • Regenerates all three published pages and bumps the analytics package to 5.1.4.
File Description
gh-pages/​lungmap/​index.html Regenerated with independent data loading and chart font updates.
gh-pages/​hca-explorer/​index.html Regenerated with independent data loading and chart font updates.
gh-pages/​anvil-explorer/​index.html Regenerated with independent data loading and chart font updates.
analytics/​uv.lock Updates the locked analytics version to 5.1.4.
analytics/​analytics_package/​pyproject.toml Bumps the analytics package version to 5.1.4.
analytics/​analytics_package/​analytics/​static_site/​template/​index.html Implements immediate fetching, chart readiness coordination, and Inter redraws.

Review effort: Balanced

- attach the chart drawing chain at startup, waiting on chartjsready and a
  chartdataready promise that loaddata resolves, so a chart.js failure that
  lands before the data is no longer reported twice
- re-render gh-pages anvil-explorer, hca-explorer and lungmap

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cc-claude-tools <cc-claude-tools@clevercanary.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

There are no active review issues blocking approval.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

frano-m added a commit to DataBiosphere/findable-ui that referenced this pull request Nov 13, 2026
…template

The Chart.js notes now follow the pattern DataBiosphere/data-browser#5035
ships, plus a catch for a blocked font file:

- Test `media === "all"` instead of `link.sheet` to tell whether the font
  stylesheet is in use, since Chrome also sets `sheet` after a failed load.
- When Inter has already loaded, create the charts in it rather than
  drawing and redrawing them.
- Take the font from `--fui-font-family`, and match the fallback stack
  to it, so a page that overrides the token gets its font in charts.
- Say to run the code after Chart.js, in a `DOMContentLoaded` listener
  when Chart.js is deferred.
- Catch a `document.fonts.load` rejection when the font file is blocked,
  leaving the charts in the fallback font.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cc-claude-tools <cc-claude-tools@clevercanary.com>
frano-m added a commit to DataBiosphere/findable-ui that referenced this pull request Nov 13, 2026
…template

The Chart.js notes now follow the pattern DataBiosphere/data-browser#5035
ships, plus a catch for a blocked font file:

- Test `media === "all"` instead of `link.sheet` to tell whether the font
  stylesheet is in use, since Chrome also sets `sheet` after a failed load.
- When Inter has already loaded, create the charts in it rather than
  drawing and redrawing them.
- Take the font from `--fui-font-family`, and match the fallback stack
  to it, so a page that overrides the token gets its font in charts.
- Say to run the code after Chart.js, in a `DOMContentLoaded` listener
  when Chart.js is deferred.
- Catch a `document.fonts.load` rejection when the font file is blocked,
  leaving the charts in the fallback font.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cc-claude-tools <cc-claude-tools@clevercanary.com>

This branch has not been deployed

No deployments
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.

Draw the analytics pages' charts in Inter, and start their data fetches without waiting for Chart.js

3 participants