feat: add explore page visualizations for 8 new data sources#78
Conversation
Design for wiring 8 new data tables into the frontend explore page with data source tabs, diverging choropleth maps, and source-specific detail charts.
16-task TDD plan covering backend API endpoints for 8 new data sources, frontend components (tabs, charts, empty states), and integration tests.
Add EPA, FBI, BLS, HUD, USDA, DOE, and Police Violence endpoints to the explore API. Each returns ExploreResponse with standardized rows. Includes state-level aggregation for tract/district data and state abbreviation-to-FIPS mapping. One test per endpoint.
…72) - Add state_fips param to DOE and police-violence endpoints (frontend sends state_fips, these models use state abbreviations) - Map police violence state abbreviation to full state name via FIPS_TO_NAME lookup for correct map tooltip display
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughImplements a comprehensive multi-source data visualization system for the Explore page, adding eight new data source endpoints (CDC health, EPA environmental justice, FBI crime, BLS labor, HUD fair housing, USDA food access, DOE civil rights, police violence) with standardized backend schemas, frontend components for filtering and visualization, type definitions, and test coverage. Changes
Sequence DiagramsequenceDiagram
participant User
participant Frontend as Explore Page<br/>(React)
participant API as Backend API<br/>(/api/explore/{source})
participant DB as Database
User->>Frontend: Select data source from tabs
activate Frontend
Frontend->>API: GET /api/explore/{source}?state_fips=...
activate API
API->>DB: Query state-level data<br/>(SELECT * FROM ...)
activate DB
DB-->>API: Return rows
deactivate DB
API->>API: Compute national_avg<br/>Extract distinct metrics/years/races
API-->>Frontend: ExploreResponse<br/>{rows, national_average,<br/>available_metrics, ...}
deactivate API
Frontend->>Frontend: Transform ExploreRow<br/>to IndicatorRow
Frontend->>Frontend: Render StateMap<br/>with diverging scale
Frontend->>Frontend: Render StateVsNationalChart<br/>for selected state
Frontend-->>User: Display visualization
deactivate Frontend
User->>Frontend: Interact with filters<br/>or state selection
activate Frontend
Frontend->>Frontend: Update derived state<br/>(filters, selectedState)
Frontend-->>User: Update charts & map
deactivate Frontend
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ 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 |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tests/test_explore_schemas.py (1)
1-10:⚠️ Potential issue | 🟡 MinorFix unsorted imports to resolve pipeline failure.
The CI pipeline reports that the import block is unsorted. Run
ruff check --fixto auto-fix the import ordering.🔧 Proposed fix
"""Tests for explore endpoint Pydantic schemas.""" + import pytest from d4bl.app.schemas import ( ExploreRow, ExploreResponse, IndicatorItem, PolicyBillItem, StateSummaryItem, )Or simply run:
ruff check --fix tests/test_explore_schemas.py🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_explore_schemas.py` around lines 1 - 10, The import block in the test file is unsorted; reorder the imports from d4bl.app.schemas so they are alphabetically sorted (e.g., ensure ExploreResponse, ExploreRow, IndicatorItem, PolicyBillItem, StateSummaryItem appear in proper alphabetical order) or run the automatic fixer (ruff check --fix tests/test_explore_schemas.py) to apply the correct import ordering; update the import statement that references ExploreRow, ExploreResponse, IndicatorItem, PolicyBillItem, and StateSummaryItem accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/d4bl/app/api.py`:
- Around line 651-660: The row dictionary constructions named row_dicts are
missing the explicit "race" key required by the ExploreRow Pydantic model;
update each row_dicts comprehension (e.g., the one shown and the similar blocks
used for CDC, EPA, HUD, USDA endpoints) to include "race": None so every
generated dict has the race field explicitly set, keeping other keys
("state_fips", "state_name", "value", "metric", "year") unchanged and preserving
compatibility with ExploreRow validation.
In `@ui-nextjs/app/explore/page.tsx`:
- Around line 137-147: The auto-selection of the first metric inside fetchData
causes a state update (setFilters) while filters is a dependency of the
surrounding useEffect, which risks a fragile re-render loop; move the
auto-selection logic out of fetchData (e.g., run once on source change) or guard
it with a ref flag (e.g., didAutoSelectMetric.current) so it only runs a single
time—update code to perform metric auto-selection in a separate handler (such as
handleSourceChange) or add a ref check before calling setFilters inside
fetchData and reset that ref when the source changes.
- Around line 253-260: Non-Census race sources pass filters.metric directly and
it can be an empty string; update the RacialGapChart and StateVsNationalChart
prop usage to provide the same fallback as the Census path by using
metric={filters.metric || 'homeownership_rate'} so the chart titles get a
sensible default when filters.metric is empty; search for RacialGapChart and
StateVsNationalChart usages in this file and replace metric={filters.metric}
with the fallback.
In `@ui-nextjs/components/explore/DataSourceTabs.tsx`:
- Around line 16-40: The button in DataSourceTabs.tsx currently lacks an
explicit type and will default to type="submit"; update the JSX button element
(the one with onClick={() => onSelect(src)} and className using isActive/src) to
include type="button" so it won't trigger form submissions, and apply the same
change to any sibling buttons in this component that are intended as non-submit
controls.
In `@ui-nextjs/components/explore/MetricFilterPanel.tsx`:
- Around line 67-82: The component currently treats empty arrays like undefined
and falls back to DEFAULT_METRICS/DEFAULT_RACES, which causes unsupported filter
values to persist; change the logic so only undefined triggers the Census
defaults (i.e., use availableMetrics === undefined ? DEFAULT_METRICS :
availableMetrics) and similarly for availableRaces/availableYears, and update
the selection normalization so that when the resolved option sets
(metrics/races) change you validate and normalize filters.metric and
filters.race (in MetricFilterPanel) to a safe value (e.g., null/first available
option) if the current filters are not in the new option list; also adjust the
radio checked=isSelected logic to reflect the normalized filters so radio groups
unselect when no valid option exists.
- Around line 95-113: The custom radio visuals are hiding keyboard focus and the
year <select> has no replacement focus style: in MetricFilterPanel (the label
key={m.value} / span with className "inline-flex items-center..." and the input
with className "sr-only") make the input a peer (keep it visually hidden) and
add focus-visible/peer-focus-visible focus styles to the radio wrapper (e.g.,
add peer on the input and peer-focus-visible or focus-within ring/border classes
to the label/span) so when the hidden radio receives keyboard focus the outer
dot gains a visible ring; do the same for the year <select> by removing any
outline suppression and adding a visible focus ring/border (e.g., focus:ring-2
focus:ring-offset-1 focus:ring-[accentColor]) so keyboard users see a clear
focus state. Ensure changes are applied inside the MetricFilterPanel component
and preserve TypeScript/functional style.
In `@ui-nextjs/components/explore/PolicyBadge.tsx`:
- Around line 20-35: The toggle button in the PolicyBadge component is missing
an explicit type and will default to "submit" in forms; update the <button>
element that uses onClick={() => setOpen(!open)} (in PolicyBadge.tsx) to include
type="button" to prevent accidental form submission when toggling the open state
for bills (references: setOpen, open, bills).
- Around line 40-55: Update the backdrop and close UX for accessibility: make
the backdrop div focusable and announceable by adding role="button" and
tabIndex={0}, attach an onKeyDown handler that calls setOpen(false) when Enter
or Space is pressed (and Escape ideally closes from the panel as well), and
ensure the sliding panel container uses role="dialog" and aria-modal="true" with
the heading given an id (referenced via aria-labelledby) using the existing
stateName heading; for the close button add type="button" and an accessible
label (aria-label="Close panel" or similar) and keep its onClick calling
setOpen(false). Ensure you reference setOpen, stateName, the backdrop div and
the close button in the changes.
In `@ui-nextjs/lib/explore-config.ts`:
- Around line 3-13: The frontend currently hardcodes FIPS_TO_ABBREV (export
const FIPS_TO_ABBREV) which duplicates the backend mapping; replace the
duplicate by sourcing the mapping from a single source of truth: add a backend
API that returns the mapping (e.g., GET /api/fips or expose the existing
src/d4bl/app/api.py mapping), then update explore-config.ts to fetch and use
that API (either at build time for static export or at runtime) or import the
mapping from a shared package/module if you can extract it into a common
library; ensure code references FIPS_TO_ABBREV are updated to use the
fetched/imported object and remove the hardcoded map.
---
Outside diff comments:
In `@tests/test_explore_schemas.py`:
- Around line 1-10: The import block in the test file is unsorted; reorder the
imports from d4bl.app.schemas so they are alphabetically sorted (e.g., ensure
ExploreResponse, ExploreRow, IndicatorItem, PolicyBillItem, StateSummaryItem
appear in proper alphabetical order) or run the automatic fixer (ruff check
--fix tests/test_explore_schemas.py) to apply the correct import ordering;
update the import statement that references ExploreRow, ExploreResponse,
IndicatorItem, PolicyBillItem, and StateSummaryItem accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 4aac70d9-83a3-49b5-b87c-7caa1f0bb03c
⛔ Files ignored due to path filters (1)
ui-nextjs/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (18)
docs/plans/2026-03-11-explore-visualizations-design.mddocs/plans/2026-03-11-explore-visualizations-plan.mdsrc/d4bl/app/api.pysrc/d4bl/app/explore_helpers.pysrc/d4bl/app/schemas.pytests/test_explore_api.pytests/test_explore_helpers.pytests/test_explore_schemas.pyui-nextjs/app/explore/page.tsxui-nextjs/components/explore/DataSourceTabs.tsxui-nextjs/components/explore/EmptyDataState.tsxui-nextjs/components/explore/MetricFilterPanel.tsxui-nextjs/components/explore/PolicyBadge.tsxui-nextjs/components/explore/StateMap.tsxui-nextjs/components/explore/StateVsNationalChart.tsxui-nextjs/lib/explore-config.tsui-nextjs/lib/types.tsui-nextjs/package.json
- Add explicit race: None to non-race endpoint row dicts - Guard metric auto-select with useRef to prevent double fetch - Add metric fallback for non-Census chart components - Add type="button" to DataSourceTabs and PolicyBadge buttons - Distinguish empty arrays from undefined in MetricFilterPanel defaults - Add keyboard focus-visible styles to radio inputs and year select - Improve PolicyBadge accessibility (aria labels, dialog role, Escape key)
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
@coderabbitai review |
Summary
Closes #72
/api/explore/cdc,/epa,/fbi,/bls,/hud,/usda,/doe,/police-violence) returning a standardizedExploreResponseshape with rows, national average, and available filter valuesStateVsNationalChartfor sources without race data,RacialGapChartfor sources with race dataPolicyBadgeslide-in panel replacing the inline policy tableEmptyDataStatecomponent for sources without ingested dataMetricFilterPanelto accept dynamic filter options per sourceTest plan
rows,national_average,available_metrics,available_years,available_racespytest tests/test_explore_api.py tests/test_explore_schemas.py tests/test_explore_helpers.py -v— 30 tests passnpm run build && npm run lint— cleanSummary by CodeRabbit
New Features
Documentation
Tests
Chores