Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"node": ">=22.0.0"
},
"dependencies": {
"@aciesai/fips-county-codes": "^1.0.4",
"@ant-design/icons": "^5.6.1",
"@auth0/auth0-react": "^2.3.0",
"@babel/preset-react": "^7.26.3",
Expand Down
146 changes: 146 additions & 0 deletions src/__tests__/pages/household/input/County.test.js
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();
});
});
140 changes: 140 additions & 0 deletions src/data/counties.js
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;
}
Loading
Loading