Skip to content

Commit d1a0c2e

Browse files
authored
Merge pull request #5640 from HHS/OPS-5574/add-all-fy-years
feat: add 'All' fiscal year option to CAN list
2 parents 8897f36 + c928ce7 commit d1a0c2e

5 files changed

Lines changed: 81 additions & 11 deletions

File tree

frontend/src/api/opsAPI.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,10 @@ export const opsApi = createApi({
299299
getCanFilterOptions: builder.query({
300300
query: ({ fiscalYear }) => {
301301
const queryParams = [];
302-
if (fiscalYear) {
303-
queryParams.push(`fiscal_year=${fiscalYear}`);
302+
if (fiscalYear && fiscalYear.length > 0) {
303+
fiscalYear.forEach((year) => {
304+
queryParams.push(`fiscal_year=${year}`);
305+
});
304306
}
305307
const queryString = queryParams.length > 0 ? `?${queryParams.join("&")}` : "";
306308
return `/cans-filters/${queryString}`;
@@ -748,8 +750,10 @@ export const opsApi = createApi({
748750
budgetMax
749751
}) => {
750752
let queryParams = [];
751-
if (fiscalYear) {
752-
queryParams.push(`fiscal_year=${fiscalYear}`);
753+
if (fiscalYear && fiscalYear.length > 0) {
754+
fiscalYear.forEach((year) => {
755+
queryParams.push(`fiscal_year=${year}`);
756+
});
753757
}
754758
if (sortConditions) {
755759
queryParams.push(`sort_conditions=${sortConditions}`);

frontend/src/api/opsAPI.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -918,7 +918,7 @@ describe("opsAPI - Wave 2 high-yield endpoint coverage", () => {
918918
);
919919

920920
const storeRef = setupApiStore(opsApi);
921-
await storeRef.store.dispatch(opsApi.endpoints.getCanFilterOptions.initiate({ fiscalYear: 2026 }));
921+
await storeRef.store.dispatch(opsApi.endpoints.getCanFilterOptions.initiate({ fiscalYear: [2026] }));
922922

923923
expect(capturedUrl).toContain("fiscal_year=2026");
924924
});

frontend/src/pages/cans/list/CANFiscalYearSelect/CANFiscalYearSelect.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const CANFiscalYearSelect = ({ fiscalYear, setSelectedFiscalYear }) => {
1313
<FiscalYear
1414
fiscalYear={fiscalYear}
1515
handleChangeFiscalYear={setSelectedFiscalYear}
16+
showAllOption={true}
1617
/>
1718
);
1819
};

frontend/src/pages/cans/list/CanList.jsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ const CanList = () => {
2626
const navigate = useNavigate();
2727
const { sortDescending, sortCondition, setSortConditions } = useSetSortConditions();
2828
const [selectedFiscalYear, setSelectedFiscalYear] = React.useState(getCurrentFiscalYear());
29-
const fiscalYear = Number(selectedFiscalYear);
3029
const [currentPage, setCurrentPage] = React.useState(1); // 1-indexed for UI
3130
const [pageSize] = React.useState(ITEMS_PER_PAGE);
3231
const [filters, setFilters] = React.useState({
@@ -37,6 +36,18 @@ const CanList = () => {
3736
budget: []
3837
});
3938

39+
// Determine fiscal year filter - send empty array when "All" is selected
40+
const getFiscalYearFilter = () => {
41+
if (selectedFiscalYear === "All") {
42+
return [];
43+
}
44+
return [Number(selectedFiscalYear)];
45+
};
46+
47+
const fiscalYearFilter = getFiscalYearFilter();
48+
// For components that need a numeric fiscal year (summary cards, display)
49+
const fiscalYear = fiscalYearFilter.length > 0 ? fiscalYearFilter[0] : undefined;
50+
4051
// Extract filter values for API
4152
const activePeriodIds = filters.activePeriod?.map((ap) => ap.id) || [];
4253
const TRANSFER_METHOD_MAP = Object.fromEntries(
@@ -55,7 +66,7 @@ const CanList = () => {
5566
isLoading,
5667
isFetching
5768
} = useGetCansQuery({
58-
fiscalYear: selectedFiscalYear,
69+
fiscalYear: fiscalYearFilter,
5970
sortConditions: sortCondition,
6071
sortDescending,
6172
page: currentPage - 1, // Convert to 0-indexed for API
@@ -75,7 +86,7 @@ const CanList = () => {
7586
isLoading: filterOptionsLoading,
7687
isFetching: filterOptionsFetching
7788
} = useGetCanFilterOptionsQuery({
78-
fiscalYear: selectedFiscalYear
89+
fiscalYear: fiscalYearFilter
7990
});
8091

8192
// Extract cans array and metadata from wrapped response
@@ -197,7 +208,7 @@ const CanList = () => {
197208
SummaryCardsSection={
198209
!isTableLoading && (
199210
<CANSummaryCards
200-
fiscalYear={fiscalYear}
211+
fiscalYear={selectedFiscalYear === "All" ? "All FYs" : fiscalYear}
201212
totalBudget={fundingSummaryData?.funding?.total_funding}
202213
newFunding={fundingSummaryData?.funding?.new_funding}
203214
carryForward={fundingSummaryData?.funding?.carry_forward_funding}

frontend/src/pages/cans/list/CanList.test.jsx

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { render, screen } from "@testing-library/react";
1+
import { render, screen, waitFor } from "@testing-library/react";
2+
import userEvent from "@testing-library/user-event";
23
import { describe, it, expect, beforeEach, vi } from "vitest";
34
import CanList from "./CanList";
45
import { useGetCanFilterOptionsQuery, useGetCansFundingQuery, useGetCansQuery } from "../../../api/opsAPI";
@@ -38,7 +39,16 @@ vi.mock("./CANFilterTags", () => ({
3839
}));
3940

4041
vi.mock("./CANFiscalYearSelect", () => ({
41-
default: () => <div data-testid="can-fiscal-year-select">FY</div>
42+
default: ({ fiscalYear, setSelectedFiscalYear }) => (
43+
<select
44+
data-testid="can-fiscal-year-select"
45+
value={fiscalYear}
46+
onChange={(e) => setSelectedFiscalYear(e.target.value)}
47+
>
48+
<option value={fiscalYear}>{fiscalYear}</option>
49+
<option value="All">All</option>
50+
</select>
51+
)
4252
}));
4353

4454
vi.mock("../../../components/UI/Table/Table.hooks", () => ({
@@ -109,4 +119,48 @@ describe("CanList", () => {
109119
expect(screen.getByTestId("can-table")).toBeInTheDocument();
110120
expect(screen.getByTestId("can-summary-cards")).toBeInTheDocument();
111121
});
122+
123+
it("sends empty fiscalYear array when 'All' is selected, resulting in no fiscal_year query param", async () => {
124+
const user = userEvent.setup();
125+
126+
render(<CanList />);
127+
128+
// Initially, should call with current fiscal year as array
129+
expect(useGetCansQuery).toHaveBeenCalledWith(
130+
expect.objectContaining({
131+
fiscalYear: expect.any(Array)
132+
})
133+
);
134+
135+
// Get the initial call's fiscalYear to verify it's not empty
136+
const initialCall = useGetCansQuery.mock.calls[0][0];
137+
expect(initialCall.fiscalYear).toHaveLength(1);
138+
139+
// Change fiscal year to "All"
140+
const fiscalYearSelect = screen.getByTestId("can-fiscal-year-select");
141+
await user.selectOptions(fiscalYearSelect, "All");
142+
143+
// Wait for the component to re-render with new params
144+
await waitFor(() => {
145+
// Find the most recent call
146+
const calls = useGetCansQuery.mock.calls;
147+
const lastCall = calls[calls.length - 1][0];
148+
149+
// Verify fiscalYear is an empty array (which results in no query param)
150+
expect(lastCall.fiscalYear).toEqual([]);
151+
});
152+
153+
// Also verify the filter options query receives empty array
154+
await waitFor(() => {
155+
const calls = useGetCanFilterOptionsQuery.mock.calls;
156+
const lastCall = calls[calls.length - 1][0];
157+
expect(lastCall.fiscalYear).toEqual([]);
158+
});
159+
160+
await waitFor(() => {
161+
const calls = useGetCansFundingQuery.mock.calls;
162+
const lastCall = calls[calls.length - 1][0];
163+
expect(lastCall.fiscalYear).toBeUndefined();
164+
});
165+
});
112166
});

0 commit comments

Comments
 (0)