Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/HHS/simpler-grants-gov into…
Browse files Browse the repository at this point in the history
… 3875/opp-package-app-tables
  • Loading branch information
babebe committed Feb 19, 2025
2 parents 2116359 + f2f4372 commit c899692
Show file tree
Hide file tree
Showing 30 changed files with 3,688 additions and 929 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci-frontend-a11y.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
- name: Start API Server for search results
run: |
cd ../api
make init db-seed-local populate-search-opportunities start &
make init db-seed-local start &
cd ../frontend
# ensure the API wait script is executable
chmod +x ../api/bin/wait-for-api.sh
Expand Down
131 changes: 131 additions & 0 deletions frontend/src/app/[locale]/search/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
"use client";

import QueryProvider from "src/app/[locale]/search/QueryProvider";
import { usePrevious } from "src/hooks/usePrevious";
import { FrontendErrorDetails } from "src/types/apiResponseTypes";
import { OptionalStringDict } from "src/types/generalTypes";
import { Breakpoints, ErrorProps } from "src/types/uiTypes";
import { convertSearchParamsToProperTypes } from "src/utils/search/convertSearchParamsToProperTypes";

import { useTranslations } from "next-intl";
import { ReadonlyURLSearchParams, useSearchParams } from "next/navigation";
import { useEffect } from "react";
import { Alert } from "@trussworks/react-uswds";

import ContentDisplayToggle from "src/components/ContentDisplayToggle";
import SearchBar from "src/components/search/SearchBar";
import SearchFilters from "src/components/search/SearchFilters";
import ServerErrorAlert from "src/components/ServerErrorAlert";

export interface ParsedError {
message: string;
searchInputs: OptionalStringDict;
status: number;
type: string;
details?: FrontendErrorDetails;
}

function isValidJSON(str: string) {
try {
JSON.parse(str);
return true;
} catch (e) {
return false; // String is not valid JSON
}
}

function createBlankParsedError(): ParsedError {
return {
type: "NetworkError",
searchInputs: {
query: "",
status: "",
fundingInstrument: "",
eligibility: "",
agency: "",
category: "",
sortby: undefined,
page: "1",
actionType: "initialLoad",
},
message: "Invalid error message JSON returned",
status: -1,
};
}

export default function SearchError({ error, reset }: ErrorProps) {
const t = useTranslations("Search");
const searchParams = useSearchParams();
const previousSearchParams =
usePrevious<ReadonlyURLSearchParams>(searchParams);

useEffect(() => {
if (
reset &&
previousSearchParams &&
searchParams.toString() !== previousSearchParams?.toString()
) {
reset();
}
}, [searchParams, reset, previousSearchParams]);

useEffect(() => {
console.error(error);
}, [error]);

// The error message is passed as an object that's been stringified.
// Parse it here.
let parsedErrorData;

if (!isValidJSON(error.message)) {
// the error likely is just a string with a non-specific Server Component error when running the built app
// "An error occurred in the Server Components render. The specific message is omitted in production builds..."
parsedErrorData = createBlankParsedError();
} else {
// Valid error thrown from server component
parsedErrorData = JSON.parse(error.message) as ParsedError;
}
const convertedSearchParams = convertSearchParamsToProperTypes(
parsedErrorData.searchInputs,
);
const { agency, category, eligibility, fundingInstrument, query, status } =
convertedSearchParams;

// note that the validation error will contain untranslated strings
const ErrorAlert =
parsedErrorData.details && parsedErrorData.type === "ValidationError" ? (
<Alert type="error" heading={t("validationError")} headingLevel="h4">
{`Error in ${parsedErrorData.details.field || "a search field"}: ${parsedErrorData.details.message || "adjust your search and try again"}`}
</Alert>
) : (
<ServerErrorAlert callToAction={t("generic_error_cta")} />
);

return (
<QueryProvider>
<div className="grid-container">
<div className="search-bar">
<SearchBar query={query} />
</div>
<div className="grid-row grid-gap">
<div className="tablet:grid-col-4">
<ContentDisplayToggle
showCallToAction={t("filterDisplayToggle.showFilters")}
hideCallToAction={t("filterDisplayToggle.hideFilters")}
breakpoint={Breakpoints.TABLET}
>
<SearchFilters
opportunityStatus={status}
eligibility={eligibility}
category={category}
fundingInstrument={fundingInstrument}
agency={agency}
/>
</ContentDisplayToggle>
</div>
<div className="tablet:grid-col-8">{ErrorAlert}</div>
</div>
</div>
</QueryProvider>
);
}
39 changes: 0 additions & 39 deletions frontend/src/components/search/SearchError.tsx

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ export interface FilterOption {
}

export interface SearchFilterAccordionProps {
filterOptions: FilterOption[];
query: Set<string>;
queryParamKey: QueryParamKey; // Ex - In query params, search?{key}=first,second,third
title: string; // Title in header of accordion
filterOptions: FilterOption[];
}

export interface FilterOptionWithChildren {
Expand Down
Loading

0 comments on commit c899692

Please sign in to comment.