-
Notifications
You must be signed in to change notification settings - Fork 146
Add county inputs #2429
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
anth-volk
merged 21 commits into
PolicyEngine:master
from
anth-volk:feat/add-county-input
Apr 3, 2025
Merged
Add county inputs #2429
Changes from 17 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
8c2a213
feat: Add county component, temporarily display, and add beginnings o…
anth-volk c7d2110
feat: Add basic implementation of county list
anth-volk 42f5cad
fix: Display state code with county name
anth-volk 6f60f6d
feat: Filter by input state
anth-volk de34bb7
feat: Filter county by state and display county name as input via search
anth-volk 06206e2
feat: Use first county as default
anth-volk bac3f42
fix: Replace fips-code package with non-buggy one and make adjustments
anth-volk ff1cb03
feat: Construct list of counties from FIPS package
anth-volk 3eca4ab
chore: Update todo list
anth-volk a1da84e
feat: Properly nest county name page
anth-volk 5782133
fix: Display full county name
anth-volk c9863e0
fix: Don't display county inputs in UK
anth-volk 6450e5c
chore: Lint
anth-volk 57cfc4a
chore: Disable lint for unused var for useSearchParams setter line
anth-volk f86900e
test: Build out list of needed tests
anth-volk 397b3d1
test: Add tests
anth-volk 187455e
chore: Remove unneeded comments
anth-volk 2e01e40
fix: Change wording on county question
anth-volk 7389b18
fix: Look up FIPS when creating County item
anth-volk 8b1d3fd
chore: Lint
anth-volk b8639fa
test: Update test to use new heading
anth-volk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,146 @@ | ||
| import { BrowserRouter } from "react-router-dom"; | ||
| import { defaultYear } from "../../../../data/constants"; | ||
| import County from "../../../../pages/household/input/County"; | ||
| import { createDefaultHousehold } from "../../../../api/variables"; | ||
| import data from "../../../__setup__/data.json"; | ||
| import { fireEvent, render, screen } from "@testing-library/react"; | ||
| import "@testing-library/jest-dom"; | ||
| import { getAllCounties } from "../../../../data/counties"; | ||
|
|
||
| const mockSetSearchParams = jest.fn(); | ||
|
|
||
| jest.mock("react-router-dom", () => { | ||
| const originalModule = jest.requireActual("react-router-dom"); | ||
| return { | ||
| __esModule: true, | ||
| ...originalModule, | ||
| useSearchParams: jest.fn(() => [{}, mockSetSearchParams]), | ||
| useNavigate: jest.fn(), | ||
| }; | ||
| }); | ||
|
|
||
| let metadataUS = data["metadataUS"]; | ||
| const allCounties = getAllCounties(); | ||
|
|
||
| describe("Given County component with default app state...", () => { | ||
| const firstCountyInUS = allCounties[0]; | ||
| const secondCountyInUS = allCounties[1]; | ||
| const householdWithoutState = createDefaultHousehold(metadataUS); | ||
|
|
||
| const props = { | ||
| metadata: metadataUS, | ||
| householdInput: householdWithoutState, | ||
| setHouseholdInput: jest.fn(), | ||
| year: defaultYear, | ||
| autoCompute: false, | ||
| }; | ||
|
|
||
| const CountyWithoutState = ( | ||
| <BrowserRouter> | ||
| <County {...props} /> | ||
| </BrowserRouter> | ||
| ); | ||
|
|
||
| test("component is displayed on render", () => { | ||
| // Given County with default app state... | ||
|
|
||
| // When component is rendered... | ||
| render(CountyWithoutState); | ||
|
|
||
| // Then correct component renders successfully | ||
| const componentHeader = "Which county do you reside in?"; | ||
| const description = screen.getByRole("heading", { | ||
| name: new RegExp(componentHeader, "i"), | ||
| }); | ||
| expect(description).toBeInTheDocument(); | ||
| }); | ||
| test("component displays expected county as default option", () => { | ||
| // When component is rendered... | ||
| render(CountyWithoutState); | ||
|
|
||
| // Then correct default county is displayed | ||
| const defaultOption = screen.getByTitle( | ||
| firstCountyInUS.getNameAndStateAbbrev(), | ||
| ); | ||
| expect(defaultOption).toBeInTheDocument(); | ||
| }); | ||
| test("on click, component displays list of selectable counties, and this list contains expected items", () => { | ||
| // When component is rendered... | ||
| render(CountyWithoutState); | ||
|
|
||
| const selectBox = screen.getByRole("combobox"); | ||
| // Simulate a click to open the select box; use older fireEvent with mouseDown | ||
| // because Ant Design Select component doesn't respond to userEvent.click | ||
| fireEvent.mouseDown(selectBox); | ||
|
|
||
| // Check that the box contains county #2 in the list | ||
| const secondCountyOption = screen.getByTitle( | ||
| secondCountyInUS.getNameAndStateAbbrev(), | ||
| ); | ||
| expect(secondCountyOption).toBeInTheDocument(); | ||
| }); | ||
| test("selecting a county updates the householdInput state", () => { | ||
| // When component is rendered... | ||
| render(CountyWithoutState); | ||
|
|
||
| // Simulate selecting a county | ||
| const selectBox = screen.getByRole("combobox"); | ||
| fireEvent.mouseDown(selectBox); | ||
|
|
||
| // Simulate selecting the second county in the list | ||
| const secondCountyOption = screen.getByTitle( | ||
| secondCountyInUS.getNameAndStateAbbrev(), | ||
| ); | ||
| secondCountyOption.click(); | ||
|
|
||
| // Check that the householdInput state was updated | ||
| expect(props.setHouseholdInput).toHaveBeenCalledWith( | ||
| expect.objectContaining({ | ||
| households: expect.objectContaining({ | ||
| "your household": expect.objectContaining({ | ||
| county_fips: { | ||
| [defaultYear]: secondCountyInUS.getFipsCode(), | ||
| }, | ||
| }), | ||
| }), | ||
| }), | ||
| ); | ||
| }); | ||
| }); | ||
|
|
||
| describe("Given County component with California selected as State...", () => { | ||
| const firstCountyInCA = allCounties.find( | ||
| (county) => county.getStateCode() === "CA", | ||
| ); | ||
|
|
||
| test("only counties in California are displayed, with first county as default", () => { | ||
| const householdWithState = createDefaultHousehold(metadataUS); | ||
| householdWithState.households["your household"] = { | ||
| state_name: { | ||
| [defaultYear]: "CA", | ||
| }, | ||
| }; | ||
|
|
||
| // Given County with default app state... | ||
| const props = { | ||
| metadata: metadataUS, | ||
| householdInput: householdWithState, | ||
| setHouseholdInput: jest.fn(), | ||
| year: defaultYear, | ||
| autoCompute: false, | ||
| }; | ||
|
|
||
| // When component is rendered... | ||
| render( | ||
| <BrowserRouter> | ||
| <County {...props} /> | ||
| </BrowserRouter>, | ||
| ); | ||
|
|
||
| // Then correct default county is displayed | ||
| const defaultOption = screen.getByTitle( | ||
| firstCountyInCA.getNameAndStateAbbrev(), | ||
| ); | ||
| expect(defaultOption).toBeInTheDocument(); | ||
| }); | ||
| }); |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| import { | ||
| getByCountyAndState, | ||
| getCountiesByState, | ||
| } from "@aciesai/fips-county-codes"; | ||
|
|
||
| export class County { | ||
| constructor(stateCode, name, fullName, fips) { | ||
| this.stateCode = stateCode; | ||
| this.name = name; | ||
| this.fullName = fullName; | ||
| this.fips = fips; | ||
| } | ||
|
|
||
| /** | ||
| * | ||
| * @returns {string} The name of a given county | ||
| */ | ||
| getShortName() { | ||
| return this.name; | ||
| } | ||
|
|
||
| getFullName() { | ||
| return this.fullName; | ||
| } | ||
|
|
||
| /** | ||
| * | ||
| * @returns {string} The two-letter state code for a given county | ||
| */ | ||
| getStateCode() { | ||
| return this.stateCode; | ||
| } | ||
|
|
||
| /** | ||
| * | ||
| * @returns {string} The name of a given county and its state abbreviation | ||
| */ | ||
| getNameAndStateAbbrev() { | ||
| return `${this.fullName}, ${this.stateCode}`; | ||
| } | ||
|
|
||
| /** | ||
| * | ||
| * @returns {string | undefined} A given county's five-digit FIPS code as a string | ||
| */ | ||
| getFipsCode() { | ||
| // This needs to be in a try-catch because the fips-county-codes package | ||
| // throws an error if it cannot find the county | ||
| try { | ||
| const dataObj = getByCountyAndState(this.stateCode, this.fullName); | ||
| return dataObj?.fips; | ||
| } catch (err) { | ||
| console.error( | ||
| "Unable to get FIPS code for county", | ||
| this.fullName, | ||
| "in state", | ||
| this.stateCode, | ||
| ); | ||
| return undefined; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| const arrStates = [ | ||
| "AL", | ||
| "AK", | ||
| "AZ", | ||
| "AR", | ||
| "CA", | ||
| "CO", | ||
| "CT", | ||
| "DE", | ||
| "DC", | ||
| "FL", | ||
| "GA", | ||
| "HI", | ||
| "ID", | ||
| "IL", | ||
| "IN", | ||
| "IA", | ||
| "KS", | ||
| "KY", | ||
| "LA", | ||
| "ME", | ||
| "MD", | ||
| "MA", | ||
| "MI", | ||
| "MN", | ||
| "MS", | ||
| "MO", | ||
| "MT", | ||
| "NE", | ||
| "NV", | ||
| "NH", | ||
| "NJ", | ||
| "NM", | ||
| "NY", | ||
| "NC", | ||
| "ND", | ||
| "OH", | ||
| "OK", | ||
| "OR", | ||
| "PA", | ||
| "RI", | ||
| "SC", | ||
| "SD", | ||
| "TN", | ||
| "TX", | ||
| "UT", | ||
| "VT", | ||
| "VA", | ||
| "WA", | ||
| "WV", | ||
| "WI", | ||
| "WY", | ||
| ]; | ||
|
|
||
| /** | ||
| * Fetch a list of all counties | ||
| * @returns {County[]} An array of all counties | ||
| */ | ||
| export function getAllCounties() { | ||
| let allStates = []; | ||
|
|
||
| for (const state of arrStates) { | ||
| const allCounties = getCountiesByState(state); | ||
| for (const county of allCounties) { | ||
| allStates.push( | ||
| new County( | ||
| state, | ||
| county.county, | ||
| county.fullname, | ||
| `${county.statefp}${county.countyfp}`, | ||
| ), | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| return allStates; | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.