Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert pathogens to app router [#1066] #1124

Merged
merged 18 commits into from
Mar 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
832e834
Initial move of /pathogens page into App Router style [#1066]
genehack Nov 13, 2024
52639e4
Initial copy of ExpandableTiles component into App Router style [#1066]
genehack Feb 12, 2025
4ad805e
Initial copy of ErrorBoundary component into App Router style [#1066]
genehack Feb 12, 2025
365f622
Initial copy of ListResources component into App Router style [#1066]
genehack Feb 12, 2025
7d32dcc
Initial copy of Spinner component into App Router style [#1066]
genehack Feb 12, 2025
7f2a4c5
Port <ExpandableTiles> component to App Router style [#1066]
genehack Feb 12, 2025
aa5ffd1
Port <ErrorBoundary> component to App Router style [#1066]
genehack Feb 12, 2025
ee705d7
Port <ListResources> types file to App Router style [#1066]
genehack Feb 12, 2025
020ea7a
Port <ListResources> dodge.js file to App Router [#1066]
genehack Feb 12, 2025
678d367
Port <ResourceModal> (part of <ListResources>) to App Router [#1066]
genehack Feb 12, 2025
9a289d1
Port <Spinner> component to App Router style [#1066]
genehack Feb 12, 2025
90708fc
Port <IndividualResource> to App Router style [#1066]
genehack Feb 13, 2025
4ce3f3c
Port <ResourceGroup> component to App Router style [#1066]
genehack Feb 13, 2025
b686a1c
Port useSortAndFilter() to App Router style [#1066]
genehack Feb 13, 2025
1c125bd
Port createFilterOptions and useFilterOptions to App Router style [#1…
genehack Feb 13, 2025
5426b24
Port useDataFetch to App Router style [#1066]
genehack Feb 13, 2025
4e65913
Port <ListResources> to App Router style [#1066]
genehack Feb 14, 2025
3a75a9a
Port /pathogens to App Router [#1066]
genehack Feb 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions static-site/app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import React from "react";

import styles from "./styles/not-found.module.css";

export default function FourOhFour(): React.ReactElement {
return (
<>
<div className={styles.errorContainer}>
<div className="errorContainer">
Oops - that page doesn’t exist! (404).
<br />
Maybe start again with <a href="/">our main page</a>?
Expand Down
23 changes: 23 additions & 0 deletions static-site/app/pathogens/callback.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ResourceListingInfo } from "../../components/list-resources/types";

/**
* A callback function used by the <ListResources> component (which
* see) to get the resources to display on the `/pathogens` page.
*
* @returns A Promise that resolves to a ResourceListingInfo
*/
export async function pathogenResourceListingCallback(): Promise<ResourceListingInfo> {
const sourceId = "core";
const sourceUrl = `list-resources/${sourceId}`;

const response = await fetch(sourceUrl, {
headers: { accept: "application/json" },
});
if (response.status !== 200) {
throw new Error(
`fetching data from "${sourceUrl}" returned status code ${response.status}`,
);
}

return (await response.json()).dataset[sourceId];
}
54 changes: 54 additions & 0 deletions static-site/app/pathogens/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from "react";

import FlexCenter from "../../components/flex-center";
import { FocusParagraphCentered } from "../../components/focus-paragraph";
import ListResources from "../../components/list-resources";
import { SmallSpacer, HugeSpacer } from "../../components/spacers";
import * as coreResources from "../../content/resource-listing.yaml";

/**
* React Server Component that generates and presents a list of
* pathogen resources.
*/
export default function Pathogens(): React.ReactElement {
return (
<>
<HugeSpacer />
<HugeSpacer />

<h1>Nextstrain-maintained pathogen analyses</h1>

<SmallSpacer />

<FlexCenter>
<FocusParagraphCentered>
These data represent analyses and situation-reports produced by the{" "}
<a href="/team">core Nextstrain team</a>. Explore analyses produced by
others on the <a href="/groups">Groups</a> and{" "}
<a href="/community">Community</a> pages.
<br />
<br />
We aim to provide a continually-updated view of publicly available
data to show pathogen evolution and epidemic spread. The pipeline used
to generate each dataset is available on{" "}
<a href="https://github.com/nextstrain/">our GitHub page</a> or by
loading a dataset and clicking the &ldquo;built with&rdquo; link at
the top of the page.
</FocusParagraphCentered>
</FlexCenter>

<HugeSpacer />

<ListResources
defaultGroupLinks
groupDisplayNames={coreResources["coreGroupDisplayNames"]}
quickLinks={coreResources["coreQuickLinks"]}
resourceType="dataset"
tileData={coreResources["coreTiles"]}
versioned
/>

<HugeSpacer />
</>
);
}
16 changes: 16 additions & 0 deletions static-site/app/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,22 @@ h6 {
font-weight: 400;
}

div.errorContainer {
color: var(--errorRed);
font-size: 28px;
font-weight: 300;
line-height: calc(1.4 * var(--niceLineHeight));
margin: 0 auto;
max-width: 640px;
padding: 130px 0 80px;
text-align: center;
}
div.errorContainer a {
color: var(--errorRed);
font-weight: 300;
text-decoration: underline;
}

div.iconParagraph {
font-size: var(--niceFontSize);
font-weight: 300;
Expand Down
15 changes: 0 additions & 15 deletions static-site/app/styles/not-found.module.css

This file was deleted.

90 changes: 90 additions & 0 deletions static-site/components/error-boundary/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React, { ErrorInfo, ReactNode } from "react";

/**
* An extension of the default `Error` class to make it possible for
* us to distinguish internally-arising Errors from others.
*
* @class
*/
export class InternalError extends Error {}

interface Props {
children: ReactNode;
}

interface State {
errorMessage: string;
hasError: boolean;
}

/**
* A React Server Component that wraps around something that might
* ow an error, and displays an error message when and if that
* happens.
*
* @class
*/
export class ErrorBoundary extends React.Component<Props, State> {
/**
* @constructor
*/
constructor(props: Props) {
super(props);
this.state = {
errorMessage: "",
hasError: false,
};
}

/**
* React-specific helper function that will be called when a child
* component throws an error during rendering. Allows for the
* display of that error.
*/
static getDerivedStateFromError(error: Error): State {
return {
errorMessage:
error instanceof InternalError
? error.message
: "Unknown error (thrown value was not an InternalError)",
hasError: true,
};
}

/**
* React-specific helper function that works with
* `getDerivedStateFromError` (which see) to enable error-message
* display.
*
* We override this to also echo the error message out the console.
*
* @override
*/
override componentDidCatch(error: Error, info: ErrorInfo) {
console.error(error);
console.error(info);
}

/**
* React-specific helper for rendering a component.
*
* Needs to be overridden to conditionally display `this.state.errorMessage`
* when `this.state.hasError` is true.
*
* @override
*/
override render() {
if (this.state.hasError) {
return (
<div className="errorContainer">
{"Something isn't working!"}
<br />
Error: `{this.state.errorMessage}`
<br />
Please <a href="/contact">get in touch</a> if this keeps happening
</div>
);
}
return this.props.children;
}
}
91 changes: 91 additions & 0 deletions static-site/components/expandable-tiles/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"use client";

import React, { useState } from "react";
import { FaChevronDown, FaChevronUp } from "react-icons/fa";

import { GenericTileBase } from "./types";

import styles from "./styles.module.css";

/**
* Number of pixels by which to pad out the container height when the
* container is collapsed
*/
const expandPreviewHeight = 60;

/**
* React Client Component to display an expandable div with a list of tiles.
*
* @param tiles - the list of tiles to display
* @param tileWidth - width of an individual tile in pixels
* @param tileHeight - height of an individual tile in pixels
* @param TileComponent - React Functional Component to render an individual tile
*/
export default function ExpandableTiles<Tile extends GenericTileBase>({
tiles,
tileWidth,
tileHeight,
TileComponent,
}: {
tiles: Tile[];
tileWidth: number;
tileHeight: number;
TileComponent: React.FunctionComponent<{ tile: Tile }>;
}): React.ReactElement {
const [isExpanded, setIsExpanded] = useState<boolean>(false);

function _toggleExpand(): void {
setIsExpanded(!isExpanded);
}

const [tilesContainerHeight, setTilesContainerHeight] = useState<number>(0);

function tilesContainerRef(tilesContainer: HTMLDivElement): void {
if (
tilesContainer &&
tilesContainerHeight !== tilesContainer.clientHeight
) {
setTilesContainerHeight(tilesContainer.clientHeight);
}
}

const isExpandable = tilesContainerHeight > tileHeight;

return (
<div>
<div
className={styles.expandableContainer}
style={{
maxHeight: isExpanded
? `${tilesContainerHeight}px`
: `${tileHeight + expandPreviewHeight}px`,
}}
>
<div
ref={tilesContainerRef}
className={styles.tilesContainer}
style={{ gridTemplateColumns: `repeat(auto-fit, ${tileWidth}px)` }}
>
{tiles.map((el) => {
return <TileComponent tile={el} key={el.name} />;
})}
</div>

{isExpandable && !isExpanded && (
<div
onClick={_toggleExpand}
className={styles.previewOverlay}
style={{ height: `${expandPreviewHeight}px` }}
/>
)}
</div>

{isExpandable && (
<div className={styles.arrowButton} onClick={_toggleExpand}>
{isExpanded ? <FaChevronUp /> : <FaChevronDown />}
</div>
)}
<div className={styles.spacer} />
</div>
);
}
38 changes: 38 additions & 0 deletions static-site/components/expandable-tiles/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.arrowButton {
cursor: pointer;
font-size: 1.8rem;
height: 1em;
text-align: center;
width: 100%;
}

.expandableContainer {
overflow-y: hidden;
position: relative;
transition: max-height 0.3s ease;
}

.previewOverlay {
background-image: linear-gradient(
to bottom,
rgba(255, 255, 255, 0) -100%,
rgba(255, 255, 255, 1) 100%
);
bottom: 0;
cursor: pointer;
left: 0;
position: absolute;
width: 100%;
z-index: 1;
}

.spacer {
min-height: 25px;
}

.tilesContainer {
display: grid;
gap: 10px;
justify-content: center;
overflow: hidden;
}
4 changes: 4 additions & 0 deletions static-site/components/expandable-tiles/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface GenericTileBase {
img: string;
name: string;
}
Loading