Midterms: House seat-distribution → discrete chart + Medalists CP#4868
Midterms: House seat-distribution → discrete chart + Medalists CP#4868aseckin wants to merge 14 commits into
Conversation
Task 1 (discrete House): derive the discrete chart's bars from the question's native outcomes (getDiscreteValueOptions) and bucket the PMF by bin index, instead of integer-rounding the raw range. The Senate renders identically (25 bars, -12..+12); the now-discrete House renders its 81 native bars (-40..+40) from its 201-point CDF, with no phantom edge bin. Task 2 (Medalists CP): both seat-distribution charts show the Medalists community prediction, fetched by reusing ServerAggregationsExplorerApi (/aggregation_explorer/, method=medalists). fetchSeatDistributions returns the post + the Medalists CDF; the chart takes a `cdfOverride` prop. No community fallback -- if Medalists is empty, the section shows the existing "unavailable" placeholder. Validated against the live project: Senate distribution centers on even, House on ~D+10; both render as native-bin discrete bars. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Fold the open-bound mass into the edge bins so a landslide beyond the chart range (e.g. Democrats winning the House by 40+) is represented at the edge instead of dropped; bars now sum to ~100%. - Move the EVEN label above the bin with a short connector line (~8px, darker than the gray bin) and invert its color to read on the chart background. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughFetches medalists aggregation CDFs, returns enriched SeatDistributionDatum, passes datum to the section, wires a ChangesMedalists CDF override feature
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
front_end/src/app/(main)/midterms-2026/helpers/fetch_dashboard_data.ts (1)
278-291: Downgrade the runtime type-guard suggestion formedalistsseat distributions
ServerAggregationsExplorerApi.getAggregationsreturns a union (AggregationExtraQuestion), so theas NumericAggregationExtraQuestioncast still narrows without checks. However,postIdfor this call is sourced only fromSEAT_DISTRIBUTION_POSTS(discrete/continuous numeric seat-distribution questions) andSeatDistributionChartalso assumespost.questionisQuestionWithNumericForecasts, so the cast should match reality here. An explicit guard is still optional if you want future-proofing (e.g., checkquestion.type !== QuestionType.MultipleChoiceor ensureaggregations.medalists.latest.forecast_valuesexists before reading).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@front_end/src/app/`(main)/midterms-2026/helpers/fetch_dashboard_data.ts around lines 278 - 291, fetchMedalistsCdf currently force-casts the result of ServerAggregationsExplorerApi.getAggregations to NumericAggregationExtraQuestion which narrows without runtime checks; update fetchMedalistsCdf so it still returns the medalists forecast_values safely by verifying the path exists before returning (check question?.aggregations?.medalists?.latest?.forecast_values or guard question.type !== QuestionType.MultipleChoice) instead of relying only on the as NumericAggregationExtraQuestion cast, and keep returning null on failure; reference ServerAggregationsExplorerApi.getAggregations, NumericAggregationExtraQuestion, and fetchMedalistsCdf to locate the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@front_end/src/app/`(main)/midterms-2026/helpers/fetch_dashboard_data.ts:
- Around line 278-291: fetchMedalistsCdf currently force-casts the result of
ServerAggregationsExplorerApi.getAggregations to NumericAggregationExtraQuestion
which narrows without runtime checks; update fetchMedalistsCdf so it still
returns the medalists forecast_values safely by verifying the path exists before
returning (check question?.aggregations?.medalists?.latest?.forecast_values or
guard question.type !== QuestionType.MultipleChoice) instead of relying only on
the as NumericAggregationExtraQuestion cast, and keep returning null on failure;
reference ServerAggregationsExplorerApi.getAggregations,
NumericAggregationExtraQuestion, and fetchMedalistsCdf to locate the change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: f63168aa-bc73-4ae2-8141-9e357c323dfc
📒 Files selected for processing (3)
front_end/src/app/(main)/midterms-2026/components/seat_distribution_chart.tsxfront_end/src/app/(main)/midterms-2026/helpers/fetch_dashboard_data.tsfront_end/src/app/(main)/midterms-2026/sections/seat_distributions.tsx
🚀 Preview EnvironmentYour preview environment is ready!
Details
ℹ️ Preview Environment InfoIsolation:
Limitations:
Cleanup:
|
Pin the leftmost/rightmost x-axis ticks to the integer bin extremes (ceil(rangeMin) / floor(rangeMax)) instead of rounding the half-integer range bounds. Math.round sends +-X.5 toward +inf (e.g. -39.5 -> -39 but +39.5 -> +40), which produced asymmetric edges; the edges now read -39 / +39 (and the Senate -12 / +12 instead of -12 / +13). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Replace the forbidden non-null assertion in the discrete tick sampling with a type-narrowing filter (the @typescript-eslint/no-non-null-assertion error failing the Frontend check). - Move the new discrete House question id (43900) into SEAT_DISTRIBUTION_POSTS in data.ts instead of a hardcoded override beside a commented-out line in the fetch helper; refresh the stale comment (both questions are Discrete now). - Guard the discrete bin mapping: it indexes the PMF 1:1 into native bins, so if a question ever serves a finer-resolution CDF (like the original continuous House question), render the unavailable placeholder instead of silently wrong bars. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
For the House chart only (gated by a new `separateOutOfBounds` prop on the section's House slot): - Push the two open-bound sentinel bins (the first/last entries, identified from the live config — x < range_min / x > range_max — not hardcoded) outward to open a gap, with a thin vertical divider line in each gap (a VictoryLine like the EVEN connector, excluded from the tooltip voronoi). Layout reads gap -> line -> gap -> bin on each side. - Relabel those bins ">N seat advantage" in the tooltip (N = round(range_max)), replacing the incorrect rounded value (e.g. "41"). New i18n key midtermsHubSeatAdvantageOverTooltip across 6 locales. The Senate is untouched (gate off). Gap size (OOB_GAP_STEPS) and divider style are tunable for iteration. Verified the target config (-40..40, 40 outcomes) by simulation: odd-int bins -39..39, OOB pushed to +-43, dividers at +-41, guard passes, OOB tooltips resolve to ">40 seat advantage". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The OOB divider lines now span 50% of the chart's vertical height instead of the full height (new tunable OOB_DIVIDER_HEIGHT_FRAC = 0.5). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
front_end/src/app/(main)/midterms-2026/sections/seat_distributions.tsx (1)
76-76:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAlign the Medalists CDF validity guard with the chart contract.
Line 76 treats any non-empty
medalistsCdfas valid, butSeatDistributionChartrejects CDFs with length< 2; that can produce a blank slot instead of the unavailable placeholder.Suggested fix
- if (!datum || !datum.medalistsCdf?.length) { + if (!datum || (datum.medalistsCdf?.length ?? 0) < 2) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@front_end/src/app/`(main)/midterms-2026/sections/seat_distributions.tsx at line 76, The validity guard on line 76 checks if medalistsCdf is non-empty, but SeatDistributionChart requires a minimum length of 2 for the CDF to be valid. Update the condition from checking !datum.medalistsCdf?.length to checking that medalistsCdf?.length is greater than or equal to 2, so that CDFs with length 1 are treated as invalid and trigger the unavailable placeholder instead of passing through to SeatDistributionChart which will then reject them and create a blank slot.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@front_end/src/app/`(main)/midterms-2026/sections/seat_distributions.tsx:
- Line 76: The validity guard on line 76 checks if medalistsCdf is non-empty,
but SeatDistributionChart requires a minimum length of 2 for the CDF to be
valid. Update the condition from checking !datum.medalistsCdf?.length to
checking that medalistsCdf?.length is greater than or equal to 2, so that CDFs
with length 1 are treated as invalid and trigger the unavailable placeholder
instead of passing through to SeatDistributionChart which will then reject them
and create a blank slot.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a88b0db8-3456-4f1a-b3ba-e9c7c9cfdc7f
📒 Files selected for processing (10)
front_end/messages/cs.jsonfront_end/messages/en.jsonfront_end/messages/es.jsonfront_end/messages/pt.jsonfront_end/messages/zh-TW.jsonfront_end/messages/zh.jsonfront_end/src/app/(main)/midterms-2026/components/seat_distribution_chart.tsxfront_end/src/app/(main)/midterms-2026/data.tsfront_end/src/app/(main)/midterms-2026/helpers/fetch_dashboard_data.tsfront_end/src/app/(main)/midterms-2026/sections/seat_distributions.tsx
✅ Files skipped from review due to trivial changes (5)
- front_end/messages/pt.json
- front_end/messages/en.json
- front_end/messages/es.json
- front_end/messages/cs.json
- front_end/src/app/(main)/midterms-2026/data.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- front_end/src/app/(main)/midterms-2026/helpers/fetch_dashboard_data.ts
- front_end/src/app/(main)/midterms-2026/components/seat_distribution_chart.tsx
Align the section's availability check with SeatDistributionChart's own minimum (cdf.length < 2 -> null): a degenerate 1-point Medalists CDF now shows the "unavailable" placeholder instead of slipping through to a blank slot. Phrased as `!datum?.medalistsCdf || length < 2` to preserve the non-null narrowing the cdfOverride prop relies on. Addresses PR review feedback. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builds on the merged 2026 US Midterms dashboard. Two coupled changes to the Seat Distribution section.
Changes
House seat-distribution rendered as a discrete chart
getDiscreteValueOptions) and buckets the PMF by bin index, instead of integer-rounding the raw range. The Senate renders identically (25 bars, -12…+12); the House renders its 81 native bars (-40…+40) from its 201-point CDF, with no phantom edge bin.Medalists CP
ServerAggregationsExplorerApi.getAggregations({ aggregationMethods: "medalists" })— no new aggregation logic, no backend change. The chart takes acdfOverrideprop;fetchSeatDistributionsreturns the post + the Medalists CDF. No community fallback — if Medalists is empty, the section shows the existing "unavailable" placeholder.🤖 Generated with Claude Code
Summary by CodeRabbit