Skip to content

Commit

Permalink
Move search to app router
Browse files Browse the repository at this point in the history
  • Loading branch information
acouch committed Feb 28, 2024
1 parent 8007004 commit 9addbcc
Show file tree
Hide file tree
Showing 8 changed files with 613 additions and 57 deletions.
258 changes: 203 additions & 55 deletions frontend/package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@
},
"dependencies": {
"@next/third-parties": "^14.0.4",
"@trussworks/react-uswds": "^5.0.0",
"@trussworks/react-uswds": "^7.0.0",
"@uswds/uswds": "^3.6.0",
"i18next": "^23.0.0",
"js-cookie": "^3.0.5",
"lodash": "^4.17.21",
"next": "^13.5.2",
"next": "^14.0.3",
"next-i18next": "^15.0.0",
"next-intl": "^3.2.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-i18next": "^14.0.0",
Expand Down
32 changes: 32 additions & 0 deletions frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Root layout component, wraps all pages.
* @see https://nextjs.org/docs/app/api-reference/file-conventions/layout
*/
import { Metadata } from "next";

import Layout from "src/components/AppLayout";

import "src/styles/styles.scss";

export const metadata: Metadata = {
icons: [`${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/img/logo.svg`],
};

interface LayoutProps {
children: React.ReactNode;
params: {
locale: string;
};
}

export default function RootLayout({ children, params }: LayoutProps) {
return (
<html lang="en">
<body>
{/* Separate layout component for the inner-body UI elements since Storybook
and tests trip over the fact that this file renders an <html> tag */}
<Layout locale="english">{children}</Layout>
</body>
</html>
);
}
60 changes: 60 additions & 0 deletions frontend/src/app/search2/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"use client";
import type { GetStaticProps, NextPage } from "next";
import { useFeatureFlags } from "src/hooks/useFeatureFlags";

import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import React, { useState } from "react";

import { APISearchFetcher } from "../../services/searchfetcher/APISearchFetcher";
import { MockSearchFetcher } from "../../services/searchfetcher/MockSearchFetcher";
import {
fetchSearchOpportunities,
SearchFetcher,
} from "../../services/searchfetcher/SearchFetcher";
import { Opportunity } from "../../types/searchTypes";
import PageNotFound from "../../pages/404";

const useMockData = true;
const searchFetcher: SearchFetcher = useMockData
? new MockSearchFetcher()
: new APISearchFetcher();

interface SearchProps {
initialOpportunities: Opportunity[];
}

const Search: NextPage<SearchProps> = ({ initialOpportunities = [] }) => {
const { featureFlagsManager, mounted } = useFeatureFlags();
const [searchResults, setSearchResults] =
useState<Opportunity[]>(initialOpportunities);

const handleButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
performSearch().catch((e) => console.log(e));
};

const performSearch = async () => {
const opportunities = await fetchSearchOpportunities(searchFetcher);
setSearchResults(opportunities);
};

if (!mounted) return null;
if (!featureFlagsManager.isFeatureEnabled("showSearchV0")) {
return <PageNotFound />;
}

return (
<>
<button onClick={handleButtonClick}>Update Results</button>
<ul>
{searchResults.map((opportunity) => (
<li key={opportunity.id}>
{opportunity.id}, {opportunity.title}
</li>
))}
</ul>
</>
);
};

export default Search;
119 changes: 119 additions & 0 deletions frontend/src/components/AppFooter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
'use client';

import { ExternalRoutes } from "src/constants/routes";
import { assetPath } from "src/utils/assetPath";

import { ComponentType } from "react";
import {
Address,
Grid,
GridContainer,
Icon,
SocialLinks,
Footer as USWDSFooter,
} from "@trussworks/react-uswds";
import { IconProps } from "@trussworks/react-uswds/lib/components/Icon/Icon";

// Recreate @trussworks/react-uswds SocialLink component to accept any Icon
// https://github.com/trussworks/react-uswds/blob/cf5b4555e25f0e52fc8af66afe29253922bed2a5/src/components/Footer/SocialLinks/SocialLinks.tsx#L33
type SocialLinkProps = {
href: string;
name: string;
Tag: ComponentType<IconProps>;
};

const SocialLink = ({ href, name, Tag }: SocialLinkProps) => (
<a className="usa-social-link" href={href} title={name} target="_blank">
<Tag className="usa-social-link__icon" name={name} aria-label={name} />
</a>
);

const Footer = () => {

const links = [
{
href: ExternalRoutes.GRANTS_TWITTER,
name: "link_twitter",
Tag: Icon.Twitter,
},
{
href: ExternalRoutes.GRANTS_YOUTUBE,
name: "link_youtube",
Tag: Icon.Youtube,
},
{
href: ExternalRoutes.GRANTS_BLOG,
name: "link_blog",
Tag: Icon.LocalLibrary,
},
{
href: ExternalRoutes.GRANTS_NEWSLETTER,
name: "link_newsletter",
Tag: Icon.Mail,
},
{
href: ExternalRoutes.GRANTS_RSS,
name: "link_rss",
Tag: Icon.RssFeed,
},
{
href: ExternalRoutes.GITHUB_REPO,
name: "link_github",
Tag: Icon.Github,
},
].map(({ href, name, Tag }) => (
<SocialLink
href={href}
key={name.toLocaleLowerCase()}
name={name}
Tag={Tag}
/>
));

return (
<USWDSFooter
data-testid="footer"
size="medium"
returnToTop={
<GridContainer className="usa-footer__return-to-top margin-top-5">
<a href="#">{"return_to_top"}</a>
</GridContainer>
}
primary={null}
secondary={
<Grid row gap>
<Grid tablet={{ col: 4 }} desktop={{ col: 6 }}>
<img
className="maxh-15 margin-bottom-2 tablet:margin-bottom-0"
alt={"logo_alt"}
src={assetPath("/img/grants-gov-logo.png")}
/>
</Grid>
<Grid
className="usa-footer__contact-links"
tablet={{ col: 8 }}
desktop={{ col: 6 }}
>
<SocialLinks links={links} />
<h2 className="usa-footer__contact-heading">
{"agency_contact_center"}
</h2>
<Address
size="medium"
items={[
<a key="telephone" href={`tel:${"telephone"}`}>
{"telephone"}
</a>,
<a key="email" href={`mailto:${ExternalRoutes.EMAIL_SUPPORT}`}>
{ExternalRoutes.EMAIL_SUPPORT}
</a>,
]}
/>
</Grid>
</Grid>
}
/>
);
};

export default Footer;
83 changes: 83 additions & 0 deletions frontend/src/components/AppGrantsIdentifier.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { ExternalRoutes } from "src/constants/routes";

import Image from "next/image";
import {
Identifier,
IdentifierGov,
IdentifierIdentity,
IdentifierLink,
IdentifierLinkItem,
IdentifierLinks,
IdentifierLogo,
IdentifierLogos,
IdentifierMasthead,
} from "@trussworks/react-uswds";

import logo from "../../public/img/logo-white-lg.webp";

const GrantsIdentifier = () => {

const logoImage = (
<Image
alt={"logo_alt"}
src={logo}
className="usa-identifier__logo-img"
/>
);

const IdentifierLinkList = [
{
href: ExternalRoutes.ABOUT_HHS,
text: "link_about",
},
{
href: ExternalRoutes.ACCESSIBILITY_COMPLIANCE,
text: "link_accessibility",
},
{
href: ExternalRoutes.FOIA,
text: "link_foia",
},
{
href: ExternalRoutes.NO_FEAR,
text: "link_fear",
},
{
href: ExternalRoutes.INSPECTOR_GENERAL,
text: "link_ig",
},
{
href: ExternalRoutes.PERFORMANCE_REPORTS,
text: "link_performance",
},
{
href: ExternalRoutes.PRIVACY_POLICY,
text: "link_privacy",
},
].map(({ text, href }) => (
<IdentifierLinkItem key={text}>
<IdentifierLink target="_blank" rel="noopener noreferrer" href={href}>
{text}
</IdentifierLink>
</IdentifierLinkItem>
));

return (
<Identifier data-testid="identifier">
<IdentifierMasthead aria-label="Agency identifier">
<IdentifierLogos>
<IdentifierLogo href="#">{logoImage}</IdentifierLogo>
</IdentifierLogos>
<IdentifierIdentity domain="HHS.gov">
</IdentifierIdentity>
</IdentifierMasthead>
<IdentifierLinks navProps={{ "aria-label": "Important links" }}>
{IdentifierLinkList}
</IdentifierLinks>
<IdentifierGov aria-label="U.S. government information and services">
</IdentifierGov>
</Identifier>
);
};

export default GrantsIdentifier;
Loading

0 comments on commit 9addbcc

Please sign in to comment.