Midterms dashboard: race summary, seat medians, engagement block, insight cards - #5114
Draft
aseckin wants to merge 1 commit into
Draft
Midterms dashboard: race summary, seat medians, engagement block, insight cards#5114aseckin wants to merge 1 commit into
aseckin wants to merge 1 commit into
Conversation
…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>
Contributor
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
Contributor
🚀 Preview EnvironmentYour preview environment is ready!
Details
ℹ️ Preview Environment InfoIsolation:
Limitations:
Cleanup:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Five improvements to the
/midterms-2026hub.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
svgTopis byte-identical across both tabs at 1024px and 1440px, including with a forced 3-line string.Lean threshold is
demWinPct >= 50, the same splitstate_tooltip.tsxalready 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
VictoryTooltipshape as the hover tooltips.computeQuartilesFromCDFwas 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.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.3. "Today:" baseline
Chamber control's pre-midterm seat counts read
Today:instead ofCurrent: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 wrappingActivityCard, which renders username and subtitle together at the top and forcespr-12for its corner arrow — both fight this layout, and a 340px card can't spare 48px of right padding once the comment is the hero.ActivityCardis 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.
NewsletterSubscribeCardgained an optionalcopyprop 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 existingcontactUs/copyLink/emailAddresskeys. Seat counts use ICU plurals so+1doesn't read "1 seats".Worth a reviewer's attention
A Victory footgun.
victory-voronoi-container'shandleMouseMovecallsonActivated(newPoints)and thenonDeactivated(previousPoints)back-to-back in the same tick whenever the active bin changes. An unconditionalonDeactivatedhandler 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 isvoronoiDimension="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 formatclean.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.