Skip to content

feat: add explore page visualizations for 8 new data sources#78

Merged
William-Hill merged 20 commits into
mainfrom
feat/explore-visualizations-72
Mar 11, 2026
Merged

feat: add explore page visualizations for 8 new data sources#78
William-Hill merged 20 commits into
mainfrom
feat/explore-visualizations-72

Conversation

@William-Hill

@William-Hill William-Hill commented Mar 11, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #72

  • Add 8 new API endpoints (/api/explore/cdc, /epa, /fbi, /bls, /hud, /usda, /doe, /police-violence) returning a standardized ExploreResponse shape with rows, national average, and available filter values
  • Add data source tab bar to the explore page so users can switch between Census ACS and the 8 new sources, each with its own accent color
  • Add diverging choropleth color scale (gray below national average, accent color above) to the state map
  • Add StateVsNationalChart for sources without race data, RacialGapChart for sources with race data
  • Add collapsible PolicyBadge slide-in panel replacing the inline policy table
  • Add EmptyDataState component for sources without ingested data
  • Generalize MetricFilterPanel to accept dynamic filter options per source
  • 30 backend tests passing, frontend builds and lints clean

Test plan

  • Verify all 8 explore endpoints return valid JSON with rows, national_average, available_metrics, available_years, available_races
  • Verify tab switching resets filters and loads new source data
  • Verify diverging color scale shows gray below average, accent above
  • Verify clicking a state shows racial breakdown (race sources) or state-vs-national chart (non-race sources)
  • Verify policy badge appears and opens slide-in panel when bills exist
  • Verify empty data state message shown for sources without ingested data
  • Run pytest tests/test_explore_api.py tests/test_explore_schemas.py tests/test_explore_helpers.py -v — 30 tests pass
  • Run npm run build && npm run lint — clean

Summary by CodeRabbit

  • New Features

    • Expanded Explore page with eight new data sources (CDC health, EPA environmental justice, FBI crime, BLS labor, HUD housing, USDA food access, DOE civil rights, police violence).
    • Added tabbed source navigation with per-source filtering, diverging choropleth maps centered on national average, and state-level comparison charts.
    • Integrated policy tracker panel for detailed policy information.
  • Documentation

    • Added design and implementation plans for explore visualizations.
  • Tests

    • Comprehensive test coverage for new API endpoints and data schemas.
  • Chores

    • Added d3-interpolate dependency for color scale support.

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
@coderabbitai

coderabbitai Bot commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@William-Hill has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 11 minutes and 38 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c40e436c-41a1-4e3e-b97b-486c91049881

📥 Commits

Reviewing files that changed from the base of the PR and between ee76a39 and 04a7912.

📒 Files selected for processing (5)
  • src/d4bl/app/api.py
  • ui-nextjs/app/explore/page.tsx
  • ui-nextjs/components/explore/DataSourceTabs.tsx
  • ui-nextjs/components/explore/MetricFilterPanel.tsx
  • ui-nextjs/components/explore/PolicyBadge.tsx
📝 Walkthrough

Walkthrough

Implements 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

Cohort / File(s) Summary
Documentation & Planning
docs/plans/2026-03-11-explore-visualizations-design.md, docs/plans/2026-03-11-explore-visualizations-plan.md
Design and implementation plan documents specifying UI layout, API contracts, component structure, and backend requirements for multi-source explore page visualizations.
Backend Data Schemas & Helpers
src/d4bl/app/schemas.py, src/d4bl/app/explore_helpers.py
Added ExploreRow and ExploreResponse Pydantic models; created explore_helpers.py with compute_national_avg and distinct_values utilities for data aggregation.
Backend API Endpoints
src/d4bl/app/api.py
Added eight new /api/explore/{source} endpoints (CDC, EPA, FBI, BLS, HUD, USDA, DOE, police) with state-level data aggregation; introduced FIPS/abbreviation mapping constants (ABBREV_TO_FIPS, FIPS_TO_ABBREV, FIPS_TO_NAME).
Frontend Type Definitions & Configuration
ui-nextjs/lib/types.ts, ui-nextjs/lib/explore-config.ts
Added ExploreRow and ExploreResponse TypeScript interfaces; created explore-config.ts with DataSourceConfig model, FIPS_TO_ABBREV mapping, toIndicatorRow transformer, and DATA_SOURCES registry.
Frontend Components
ui-nextjs/components/explore/DataSourceTabs.tsx, ui-nextjs/components/explore/EmptyDataState.tsx, ui-nextjs/components/explore/StateVsNationalChart.tsx, ui-nextjs/components/explore/PolicyBadge.tsx, ui-nextjs/components/explore/StateMap.tsx, ui-nextjs/components/explore/MetricFilterPanel.tsx
New tabbed source navigation, empty state UI, and state-vs-national comparison chart; added policy badge panel; extended StateMap with accent color and diverging color scale support; generalized MetricFilterPanel to accept dynamic filter options (metrics, years, races).
Frontend Page Orchestration
ui-nextjs/app/explore/page.tsx
Refactored to support multi-source data handling with activeSource state, per-source data fetching, dynamic component composition, policy data integration, and conditional rendering based on source configuration.
Test Coverage
tests/test_explore_api.py, tests/test_explore_helpers.py, tests/test_explore_schemas.py
Added comprehensive test suites for new API endpoints, helper utilities, and Pydantic schemas with mocked database interactions and response validation.
Dependencies
ui-nextjs/package.json
Added d3-interpolate (production) and @types/d3-interpolate (development) for diverging color scale support.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • William-Hill/d4bl_ai_agent#44: Modifies explore frontend components (page.tsx, StateMap.tsx) with import and type definition changes that may interact with this PR's component refactoring.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 34.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main change: adding explore page visualizations for 8 new data sources, which is the core objective of this PR.
Linked Issues check ✅ Passed The PR fully implements requirements from issue #72: all 8 data sources (CDC, EPA, FBI, BLS, HUD, USDA, DOE, police-violence) are integrated with backend endpoints, frontend components, charts, maps, filters, and state-level views.
Out of Scope Changes check ✅ Passed All changes are within scope: backend adds 8 explore endpoints and schemas; frontend adds source switching, visualizations, color scales, and filter components; documentation provides design/implementation plans; tests verify functionality. No unrelated changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/explore-visualizations-72

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 and usage tips.

@William-Hill

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Mar 11, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot 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.

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 | 🟡 Minor

Fix unsorted imports to resolve pipeline failure.

The CI pipeline reports that the import block is unsorted. Run ruff check --fix to 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

📥 Commits

Reviewing files that changed from the base of the PR and between b57b014 and ee76a39.

⛔ Files ignored due to path filters (1)
  • ui-nextjs/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (18)
  • docs/plans/2026-03-11-explore-visualizations-design.md
  • docs/plans/2026-03-11-explore-visualizations-plan.md
  • src/d4bl/app/api.py
  • src/d4bl/app/explore_helpers.py
  • src/d4bl/app/schemas.py
  • tests/test_explore_api.py
  • tests/test_explore_helpers.py
  • tests/test_explore_schemas.py
  • ui-nextjs/app/explore/page.tsx
  • ui-nextjs/components/explore/DataSourceTabs.tsx
  • ui-nextjs/components/explore/EmptyDataState.tsx
  • ui-nextjs/components/explore/MetricFilterPanel.tsx
  • ui-nextjs/components/explore/PolicyBadge.tsx
  • ui-nextjs/components/explore/StateMap.tsx
  • ui-nextjs/components/explore/StateVsNationalChart.tsx
  • ui-nextjs/lib/explore-config.ts
  • ui-nextjs/lib/types.ts
  • ui-nextjs/package.json

Comment thread src/d4bl/app/api.py
Comment thread ui-nextjs/app/explore/page.tsx
Comment thread ui-nextjs/app/explore/page.tsx
Comment thread ui-nextjs/components/explore/DataSourceTabs.tsx
Comment thread ui-nextjs/components/explore/MetricFilterPanel.tsx Outdated
Comment thread ui-nextjs/components/explore/MetricFilterPanel.tsx Outdated
Comment thread ui-nextjs/components/explore/PolicyBadge.tsx
Comment thread ui-nextjs/components/explore/PolicyBadge.tsx
Comment thread ui-nextjs/lib/explore-config.ts
- 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)
@William-Hill

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Mar 11, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@William-Hill

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@William-Hill William-Hill merged commit 6e83905 into main Mar 11, 2026
2 of 3 checks passed
@William-Hill William-Hill deleted the feat/explore-visualizations-72 branch March 11, 2026 18:30
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.

Add explore page visualizations for new data sources

1 participant