Skip to content
This repository has been archived by the owner on Mar 20, 2024. It is now read-only.

Commit

Permalink
Add pagination ability (#192)
Browse files Browse the repository at this point in the history
* add pagination ability; needs rework with filters

* remove icon

* fixup filtering and tests
  • Loading branch information
gmrabian authored Mar 20, 2024
1 parent e9f9530 commit 9160e51
Show file tree
Hide file tree
Showing 16 changed files with 240 additions and 139 deletions.
13 changes: 5 additions & 8 deletions src/app/[id]/apply/confirmation/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,17 @@ import ErrorPage from "@/src/app/error/page";
// types
import { College } from "@/src/app/types";

const filterCollege = (institutionsArray: College[], id: number) => {
return institutionsArray.filter((college) => college.id == id)[0];
};

export default function ConfirmationPage({ params }: Props) {
const { institutionsArray } = useContext(InstitutionContext);
const { institutionsObject } = useContext(InstitutionContext);
const [loading, setLoading] = useState(true);
const [selectedCollege, setSelectedCollege] = useState<College>();

useEffect(() => {
if (institutionsArray) {
setSelectedCollege(filterCollege(institutionsArray!, params.id));
if (institutionsObject) {
setSelectedCollege(institutionsObject[params.id]);
setLoading(false);
}
}, [institutionsArray, params.id]);
}, [institutionsObject, params.id]);

const View = !selectedCollege ? (
<ErrorPage />
Expand Down
14 changes: 5 additions & 9 deletions src/app/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,26 @@ import NotFound from "@/src/app/not-found";
// types
import { College } from "../types";

const filterCollege = (institutionsArray: College[], id: number) => {
return institutionsArray.filter((college) => college.id == id)[0];
};

export default function InstitutionDetails({
params,
}: {
params: { id: number };
}) {
const { institutionsArray } = useContext(InstitutionContext);
const { institutionsObject } = useContext(InstitutionContext);
const [loading, setLoading] = useState(true);
const [selectedCollege, setSelectedCollege] = useState<College>();
useEffect(() => {
if (institutionsArray) {
setSelectedCollege(filterCollege(institutionsArray!, params.id));
if (institutionsObject) {
setSelectedCollege(institutionsObject[params.id]);
setLoading(false);
}
}, [institutionsArray, params.id]);
}, [institutionsObject, params.id]);

const View = !selectedCollege ? (
<NotFound />
) : (
<ul className="usa-card-group details_page">
<BannerCard key={params.id} college={selectedCollege} />
<BannerCard key={params.id} college={selectedCollege} id={params.id} />
<DetailsCards key={`${params.id}-dets`} college={selectedCollege} />
</ul>
);
Expand Down
10 changes: 0 additions & 10 deletions src/app/assets/icons/arrow_upward.svg

This file was deleted.

7 changes: 4 additions & 3 deletions src/app/components/cards/BannerCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ import {
//types
import { College } from "../../types";

export const BannerCard = ({ college }: Props) => {
export const BannerCard = ({ college, id }: Props) => {
return (
<Card layout="flagDefault" headerFirst={true} className="card card_banner">
<div className="card_banner-container">
<CardMedia className="card_banner-image">
<ImageWithFallback
src={`https://swift-institution-images-public.s3.amazonaws.com/${college.id}.png`}
src={`https://swift-institution-images-public.s3.amazonaws.com/${id}.png`}
alt={`AI generated image of ${college.name}`}
width={400}
height={400}
Expand Down Expand Up @@ -64,7 +64,7 @@ export const BannerCard = ({ college }: Props) => {
<CardFooter>
<Link
className="usa-button card_banner-apply-button"
href={`${college.id}/apply`}
href={`${id}/apply`}
onClick={() => mixpanel.track("click_launch-application")}
aria-label={`Apply to ${college.name}`}
>
Expand All @@ -79,4 +79,5 @@ export const BannerCard = ({ college }: Props) => {

type Props = {
college: College;
id: number;
};
10 changes: 5 additions & 5 deletions src/app/components/form/AppForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export const AppForm = ({ institutionId }: Props) => {
const ApplicationView = !application ? (
<NotFound />
) : (
<main className="application">
<div className="application">
<USWDSForm
initialValues={{
"first-name": "",
Expand Down Expand Up @@ -187,7 +187,7 @@ export const AppForm = ({ institutionId }: Props) => {
<TextArea
id={"essay-question-1"}
label={essayQ1}
name={"question-1"}
name={"essay-question-1"}
required={false}
/>
</fieldset>
Expand All @@ -204,7 +204,7 @@ export const AppForm = ({ institutionId }: Props) => {
<TextArea
id={"essay-question-2"}
label={essayQ2}
name={"question-2"}
name={"essay-question-2"}
required={false}
/>
</fieldset>
Expand All @@ -222,7 +222,7 @@ export const AppForm = ({ institutionId }: Props) => {
<TextArea
id={"essay-question-3"}
label={essayQ3}
name={"question-3"}
name={"essay-question-3"}
required={false}
/>
</fieldset>
Expand All @@ -239,7 +239,7 @@ export const AppForm = ({ institutionId }: Props) => {
</ButtonGroup>
</div>
</USWDSForm>
</main>
</div>
);

return <main>{loading ? <Spinner /> : ApplicationView}</main>;
Expand Down
123 changes: 84 additions & 39 deletions src/app/components/layout/Browse.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,37 @@
import { useContext, useEffect, useState } from "react";
// components
import { Button } from "@trussworks/react-uswds";
import Image from "next/image";
import {
CollegeCard,
FilterModal,
InstitutionContext,
Spinner,
USWDSForm,
} from "../../components";
// types
import { College } from "../../types";
// utils
import { filterInstitutions } from "../../utils/filtering";
import { College } from "../../types";
// icons
import arrow_upward from "../../assets/icons/arrow_upward.svg";
import { get20Institutions } from "../../utils/institutions";

const defaultFilters = {
"filter-type": ["Public", "Private nonprofit", "Private for-profit"],
"filter-undergrad-pop": ["<2", "2-5", "5-10", "10-20", ">20"],
"filter-avg-cost-per-year": ["<10$", "10-20$", "20-40$", "40-60$", ">60$"],
"filter-state": "- Select -",
"filter-grad-rate": [">90", "60-90", "30-60", "<30"],
};

export const Browse = () => {
const { filteredInstitutions, institutionsArray, setFilteredInstitutions } =
const { institutionsArray, setFilteredInstitutions } =
useContext(InstitutionContext);
const [instArray, setInstArray] = useState<College[]>([]);

const [loading, setLoading] = useState<boolean>(true);
const [lastScannedKey, setLastScannedKey] = useState<any | undefined>();
const [scrollPosition, setScrollPosition] = useState<boolean>(false);
const [isModalVisible, setIsModalVisible] = useState<boolean>(false);
const [showPagination, setShowPagination] = useState<boolean>(true);

useEffect(() => {
const onScroll = (e: any) => {
Expand All @@ -39,56 +51,89 @@ export const Browse = () => {
};

const applyFilters = async (filters: any) => {
const filteredInstitutions = filterInstitutions(
institutionsArray!,
filters,
);
setFilteredInstitutions(filteredInstitutions);
if (JSON.stringify(filters) === JSON.stringify(defaultFilters)) {
await load20Institutions(true);
} else {
const filteredColleges = filterInstitutions(institutionsArray!, filters);
setFilteredInstitutions(filteredColleges);
setInstArray(filteredColleges);
setLastScannedKey(undefined);
setShowPagination(false);
}
closeModal();
};

if (!filteredInstitutions) return <Spinner />;
const load20Institutions = async (shouldReset?: boolean) => {
try {
let newArray;
const { colleges: result, lastKey } =
await get20Institutions(lastScannedKey);
if (shouldReset) {
newArray = Array(...result);
} else {
newArray = Array(...instArray, ...result);
}
setInstArray(newArray);
setLastScannedKey(lastKey);
setShowPagination(true);
setLoading(false);
} catch (e: any) {
throw new Error("Institution data could not be loaded.");
}
};

useEffect(() => {
// loads initial 20 institutions to display
load20Institutions();
}, []); // eslint-disable-line react-hooks/exhaustive-deps

const InstList = () => {
return (
<ul className="usa-card-group">
{instArray!.map((school: College) => (
<CollegeCard key={school.id} college={school} />
))}
</ul>
);
};

if (loading) return <Spinner />;
return (
<>
{filteredInstitutions && (
<div className="browse_header">
<h2 className="browse_header-title">Browse colleges</h2>
<p className="site_text-intro browse_header-subtitle">
Find the college that’s right for you
</p>
<Button type="button" outline={true} onClick={launchModal}>
Add filters
</Button>
</div>
)}
<USWDSForm initialValues={{}} submit={applyFilters}>
<div className="browse_header">
<h2 className="browse_header-title">Browse colleges</h2>
<p className="site_text-intro browse_header-subtitle">
Find the college that’s right for you
</p>
<Button type="button" outline={true} onClick={launchModal}>
Add filters
</Button>
</div>
<USWDSForm initialValues={defaultFilters} submit={applyFilters}>
{isModalVisible && <FilterModal closeHandler={closeModal} />}
</USWDSForm>
{filteredInstitutions?.length > 0 ? (
<ul className="usa-card-group">
{filteredInstitutions.map((school: College) => (
<CollegeCard key={school.id} college={school} />
))}
</ul>
{instArray.length > 0 ? (
InstList()
) : (
<p className="site_text-intro browse_header-subtitle">
No matches found for filters.
</p>
)}
{filteredInstitutions.length > 0 && scrollPosition && (
{showPagination && (
<Button
type="button"
className="browse_load-more-button"
onClick={() => load20Institutions()}
>
Load more
</Button>
)}
{scrollPosition && (
<Button
type="button"
className="browse_back-to-top-button"
className="usa-button usa-button--outline usa-button--unstyled browse_back-to-top-button"
onClick={() => window.scrollTo(0, 0)}
>
<Image
src={arrow_upward}
className="browse_back-to-top-button-icon"
height="16"
width="16"
alt="arrow_upward icon"
loading="eager"
/>
Back to Top
</Button>
)}
Expand Down
13 changes: 10 additions & 3 deletions src/app/components/modals/FilterModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { MouseEventHandler } from "react";
import { MouseEventHandler, useContext } from "react";
//components
import Image from "next/image";
import {
Expand All @@ -9,13 +9,18 @@ import {
ModalFooter,
ModalHeading,
} from "@trussworks/react-uswds";
import { CheckboxField, DropdownField } from "../../components";
import {
CheckboxField,
DropdownField,
InstitutionContext,
} from "../../components";
//assets
import close from "../../assets/icons/close.svg";
//types
import { CollegeType, stateOptions } from "../../types";

export const FilterModal = ({ closeHandler }: Props) => {
const { institutionsArray } = useContext(InstitutionContext);
return (
<div
role="dialog"
Expand Down Expand Up @@ -90,7 +95,9 @@ export const FilterModal = ({ closeHandler }: Props) => {
</div>
<ModalFooter>
<ButtonGroup>
<Button type="submit">Apply Filters</Button>
<Button type="submit" disabled={!institutionsArray}>
{!institutionsArray ? "Loading data" : "Apply Filters"}
</Button>
<Button
type="button"
onClick={closeHandler as MouseEventHandler}
Expand Down
15 changes: 9 additions & 6 deletions src/app/styles/browse.scss
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@
}
}

.browse_back-to-top-button {
position: sticky;
.browse_load-more-button {
bottom: 24px;
margin: auto;
display: flex;
Expand All @@ -57,9 +56,13 @@
box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25);
font-weight: 700;
transition: all 0.3s ease;
}

&-icon {
@include u-margin(0.5);
@include u-margin-right(1);
}
.browse_back-to-top-button {
margin: auto;
padding: 1rem 0;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.3s ease;
}
3 changes: 3 additions & 0 deletions src/app/testing/jest/frontend/ConfirmationPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ const testParams = {

const testContext: InstitutionContextShape = {
institutionsArray: [mockCollege],
institutionsObject: { 0: mockCollege },
filteredInstitutions: [mockCollege],
setFilteredInstitutions: () => {},
};

const testConfirmationPageComponent = () => (
Expand Down
Loading

0 comments on commit 9160e51

Please sign in to comment.