Skip to content

Commit

Permalink
[Issue #2770] deduplicate search requests (#2797)
Browse files Browse the repository at this point in the history
* make one request to search on page level and pass the unresolved Promise down to each child component
  • Loading branch information
doug-s-nava authored Nov 12, 2024
1 parent b9b8ff8 commit 5654a94
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 28 deletions.
18 changes: 10 additions & 8 deletions frontend/src/components/search/SearchPaginationFetch.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
"use server";

import fetchers from "src/app/api/Fetchers";
import { QueryParamData } from "src/types/search/searchRequestTypes";
import { SearchAPIResponse } from "src/types/search/searchResponseTypes";

import SearchPagination from "src/components/search/SearchPagination";

interface SearchPaginationProps {
searchParams: QueryParamData;
searchResultsPromise: Promise<SearchAPIResponse>;
// Determines whether clicking on pager items causes a scroll to the top of the search
// results. Created so the bottom pager can scroll.
scroll: boolean;
page: number;
query?: string | null;
}

export default async function SearchPaginationFetch({
searchParams,
page,
query,
searchResultsPromise,
scroll,
}: SearchPaginationProps) {
const searchResults =
await fetchers.searchOpportunityFetcher.searchOpportunities(searchParams);
const searchResults = await searchResultsPromise;
const totalPages = searchResults.pagination_info?.total_pages;
const totalResults = searchResults.pagination_info?.total_records;

return (
<>
<SearchPagination
total={totalPages}
page={searchParams.page}
query={searchParams.query}
page={page}
query={query}
scroll={scroll}
totalResults={String(totalResults)}
/>
Expand Down
27 changes: 19 additions & 8 deletions frontend/src/components/search/SearchResults.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Loading from "src/app/[locale]/search/loading";
import fetchers from "src/app/api/Fetchers";
import { QueryParamData } from "src/types/search/searchRequestTypes";

import { Suspense } from "react";
Expand All @@ -20,19 +21,19 @@ export default function SearchResults({
}) {
const { page, sortby } = searchParams;

const searchResultsPromise =
fetchers.searchOpportunityFetcher.searchOpportunities(searchParams);

const key = Object.entries(searchParams).join(",");
const pager1key = Object.entries(searchParams).join("-") + "pager1";
const pager2key = Object.entries(searchParams).join("-") + "pager2";
return (
<>
<Suspense
key={key}
fallback={<SearchResultsHeader sortby={sortby} loading={false} />}
>
<Suspense key={key} fallback={<SearchResultsHeader sortby={sortby} />}>
<SearchResultsHeaderFetch
sortby={sortby}
queryTerm={query}
searchParams={searchParams}
searchResultsPromise={searchResultsPromise}
/>
</Suspense>
<div className="usa-prose">
Expand All @@ -42,18 +43,28 @@ export default function SearchResults({
<SearchPagination loading={true} page={page} query={query} />
}
>
<SearchPaginationFetch searchParams={searchParams} scroll={false} />
<SearchPaginationFetch
page={page}
query={query}
searchResultsPromise={searchResultsPromise}
scroll={false}
/>
</Suspense>
<Suspense key={key} fallback={<Loading message={loadingMessage} />}>
<SearchResultsListFetch searchParams={searchParams} />
<SearchResultsListFetch searchResultsPromise={searchResultsPromise} />
</Suspense>
<Suspense
key={pager2key}
fallback={
<SearchPagination loading={true} page={page} query={query} />
}
>
<SearchPaginationFetch searchParams={searchParams} scroll={true} />
<SearchPaginationFetch
page={page}
query={query}
searchResultsPromise={searchResultsPromise}
scroll={true}
/>
</Suspense>
</div>
</>
Expand Down
10 changes: 4 additions & 6 deletions frontend/src/components/search/SearchResultsHeaderFetch.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
"use server";

import fetchers from "src/app/api/Fetchers";
import { QueryParamData } from "src/types/search/searchRequestTypes";
import { SearchAPIResponse } from "src/types/search/searchResponseTypes";

import SearchResultsHeader from "./SearchResultsHeader";

export default async function SearchResultsHeaderFetch({
searchParams,
searchResultsPromise,
sortby,
queryTerm,
}: {
searchParams: QueryParamData;
searchResultsPromise: Promise<SearchAPIResponse>;
sortby: string | null;
queryTerm: string | null | undefined;
}) {
const searchResults =
await fetchers.searchOpportunityFetcher.searchOpportunities(searchParams);
const searchResults = await searchResultsPromise;
const totalResults = searchResults.pagination_info?.total_records;

return (
Expand Down
10 changes: 4 additions & 6 deletions frontend/src/components/search/SearchResultsListFetch.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import fetchers from "src/app/api/Fetchers";
import { QueryParamData } from "src/types/search/searchRequestTypes";
import { SearchAPIResponse } from "src/types/search/searchResponseTypes";

import { getTranslations } from "next-intl/server";

import SearchResultsListItem from "src/components/search/SearchResultsListItem";
import ServerErrorAlert from "src/components/ServerErrorAlert";

interface ServerPageProps {
searchParams: QueryParamData;
searchResultsPromise: Promise<SearchAPIResponse>;
}

export default async function SearchResultsListFetch({
searchParams,
searchResultsPromise,
}: ServerPageProps) {
const searchResults =
await fetchers.searchOpportunityFetcher.searchOpportunities(searchParams);
const searchResults = await searchResultsPromise;
const maxPaginationError = null;
const t = await getTranslations("Search");

Expand Down
6 changes: 6 additions & 0 deletions frontend/tests/components/search/SearchResults.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ jest.mock("react", () => ({
Suspense: ({ fallback }: { fallback: React.Component }) => fallback,
}));

jest.mock("src/app/api/Fetchers", () => ({
searchOpportunityFetcher: {
searchOpportunities: jest.fn(() => Promise.resolve()),
},
}));

describe("SearchResults", () => {
it("Renders without errors", () => {
render(
Expand Down
16 changes: 16 additions & 0 deletions frontend/tests/pages/search/page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,23 @@ jest.mock("react", () => ({
Suspense: ({ fallback }: { fallback: React.Component }) => fallback,
}));

const fetchMock = jest.fn().mockResolvedValue({
json: jest.fn().mockResolvedValue({ data: [], errors: [], warnings: [] }),
ok: true,
status: 200,
});

describe("Search Route", () => {
let originalFetch: typeof global.fetch;
beforeAll(() => {
originalFetch = global.fetch;
});
afterAll(() => {
global.fetch = originalFetch;
});
beforeEach(() => {
global.fetch = fetchMock;
});
it("renders the search page with expected checkboxes checked", async () => {
const mockSearchParams = {
status: "forecasted,posted",
Expand Down

0 comments on commit 5654a94

Please sign in to comment.