Skip to content

Midterms dashboard: race summary, seat medians, engagement block, insight cards - #5114

Draft
aseckin wants to merge 1 commit into
mainfrom
2026-midterms-v2
Draft

Midterms dashboard: race summary, seat medians, engagement block, insight cards#5114
aseckin wants to merge 1 commit into
mainfrom
2026-midterms-v2

Conversation

@aseckin

@aseckin aseckin commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Five improvements to the /midterms-2026 hub.

1. Governor-tab race summary

A top-line read on the races — "10 of 19 races lean Democrat" — sitting between the chamber tabs and the legend. The count and party name are party-colored and follow whichever side leads, so this flips to red on its own if forecasts move.

The tabs and legend used to be two independent corner overlays; they're now one absolutely-positioned header row with the summary between them. Because the row is absolute, a summary that wraps grows over the map instead of pushing it down — verified the map's svgTop is byte-identical across both tabs at 1024px and 1440px, including with a forced 3-line string.

Lean threshold is demWinPct >= 50, the same split state_tooltip.tsx already uses, so the summary can never disagree with a tooltip. Governor tab only.

2. Seat-distribution medians

Both charts now surface a median: a dot on the x-axis at the median bin carrying an always-on flyout, using the same VictoryTooltip shape as the hover tooltips. computeQuartilesFromCDF was already running every render and its result was being thrown away — the quartile lines that consumed it are gated behind !isDiscrete, and both questions became Discrete in e82e6a9.

  • Hides while a bin is hovered, so it never stacks with the hover tooltip.
  • Suppressed entirely when the median is exactly 0. The EVEN bin already owns that x-position, and labelling both there invites reading them as the same claim — they aren't. The EVEN bar's height is P(seat advantage = 0); the median is where the CDF crosses 50%. They coincide on Senate today only because the community's median forecast genuinely is a tied Senate. The callout returns on its own as soon as the median moves off zero.
  • Flyout inks key off the flyout's own background rather than the page theme — the light page's black flyout needs the swatches designed for dark surfaces, and vice versa.
  • Hover flyouts to 90% opacity, median to 80%, both +1px padding-top and ~2px more gap between lines.

3. "Today:" baseline

Chamber control's pre-midterm seat counts read Today: instead of Current: and drop their party colors for gray, so the static baseline reads as context rather than competing with the live forecast directly above it. Also fixes a latent dark-mode bug: those two spans hardcoded light-mode hexes with no dark variant.

4. Community Insight cards

Cards now carry the question they're about. Three zones: question-title strip → comment as the visual hero → author footer. Height is capped rather than fixed, so a short comment lets the card shrink; the title strip wraps to a second line only when it needs to.

Rewritten as a self-contained <Link> rather than wrapping ActivityCard, which renders username and subtitle together at the top and forces pr-12 for its corner arrow — both fight this layout, and a 340px card can't spare 48px of right padding once the comment is the hero. ActivityCard is untouched, so the Labor Hub is unaffected.

5. Engagement block

The Labor Hub's three cards (contact / share / newsletter) carried over with midterms copy and its own Mailjet list, slotted between Community Insights and the footer disclaimer. NewsletterSubscribeCard gained an optional copy prop defaulting to its current English strings, so the Labor Hub renders unchanged while Midterms gets translated copy.

All new strings are in en, es, cs, pt, zh, zh-TW, translated rather than English-filled, reusing the existing contactUs / copyLink / emailAddress keys. Seat counts use ICU plurals so +1 doesn't read "1 seats".


Worth a reviewer's attention

A Victory footgun. victory-voronoi-container's handleMouseMove calls onActivated(newPoints) and then onDeactivated(previousPoints) back-to-back in the same tick whenever the active bin changes. An unconditional onDeactivated handler therefore undoes every activation and the state never sticks. Guarded with a ref that lets only the genuine clears (out-of-bounds, pointer off the container) through. Caught this because the median visibly failed to hide on hover.

The median marker must stay in voronoiBlacklist. The container is voronoiDimension="x", so without it the marker becomes its own hover target and renders a second, wrong tooltip.

Verification

bun run lint (eslint + tsc) exits 0 with 165 warnings — the same count as before these changes, so no new warnings. bun run format clean.

Manually verified against the dev server in both light and dark mode: median inks invert correctly against the flyout, hover gating survives moving between bins and restores on exit, Senate keeps EVEN with no median while House keeps its callout, all 14 insight cards land within the height cap with zero text clipping, and the summary wraps without shifting the map.

Out of scope

Noticed while testing: the page scrolls ~74px horizontally at 1024px width. The cause is the global site footer (gap-[122px] sm:gap-[218px]), outside <main> and unrelated to this work — worth a separate fix.

…ight cards

Five changes to the /midterms-2026 hub:

- Governor tab gets a top-line summary ("10 of 19 races lean Democrat"),
  rendered in a new absolutely-positioned header row shared with the chamber
  tabs and legend so a wrapping summary never pushes the map down. Lean split
  is >= 50%, matching the state tooltip so the two can't disagree.
- Both seat-distribution charts gain a median callout: a dot on the x-axis at
  the median bin carrying an always-on VictoryTooltip. It hides while a bin is
  hovered so it never stacks with the hover tooltip, and is suppressed entirely
  when the median is exactly 0 (the EVEN bin already owns that spot). Hover
  flyouts go to 90% opacity, the median's to 80%, both +1px padding-top and
  ~2px more gap between lines.
- Chamber control's pre-midterm baseline reads "Today:" instead of "Current:"
  and drops its party colors for gray, so it reads as context rather than
  competing with the live forecast above it. Also fixes an existing dark-mode
  bug: those two spans hardcoded light-mode hexes with no dark variant.
- Community Insight cards carry their question title in a header strip, with
  the comment as the visual hero and the author as a footer. Height is capped
  rather than fixed, so short comments let the card shrink.
- Adds the Labor Hub's three engagement cards (contact / share / newsletter)
  with midterms copy and its own Mailjet list. NewsletterSubscribeCard gained
  an optional `copy` prop defaulting to its current English, so the Labor Hub
  renders unchanged.

Median-marker note: Victory's voronoi handleMouseMove calls onActivated(new)
then onDeactivated(previous) back-to-back in the same tick, so an unconditional
onDeactivated handler silently undoes every activation. Guarded with a ref.

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

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 931b50dd-95d6-4acd-8e50-9b11e1f8c379

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

🚀 Preview Environment

Your preview environment is ready!

Resource Details
🌐 Preview URL https://metaculus-pr-5114-2026-midterms-v2-preview.mtcl.cc
📦 Docker Image ghcr.io/metaculus/metaculus:2026-midterms-v2-1558c85
🗄️ PostgreSQL NeonDB branch preview/pr-5114-2026-midterms-v2
Redis Fly Redis mtc-redis-pr-5114-2026-midterms-v2

Details

  • Commit: 1558c856975a11f17d995a3f9110efd980c11e79
  • Branch: 2026-midterms-v2
  • Fly App: metaculus-pr-5114-2026-midterms-v2

ℹ️ Preview Environment Info

Isolation:

  • PostgreSQL and Redis are fully isolated from production
  • Each PR gets its own database branch and Redis instance
  • Changes pushed to this PR will trigger a new deployment

Limitations:

  • Background workers and cron jobs are not deployed in preview environments
  • If you need to test background jobs, use Heroku staging environments

Cleanup:

  • This preview will be automatically destroyed when the PR is closed

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.

1 participant