Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9ceadc8
Remediate comments in `static-site/app/blog` tree [#1133]
genehack Mar 21, 2025
c4da8f4
Remediate comment in `static-site/app/contact/page.tsx` [#1133]
genehack Mar 21, 2025
7fcfab2
Remediate comments in base-level `static-site/app/` files [#1133]
genehack Mar 21, 2025
5ba6c96
Add Metadata to `static-site/app/pathogens/page.tsx` [#1133]
genehack Mar 21, 2025
cf45081
Remediate comment in `static-site/app/team/page.tsx` [#1133]
genehack Mar 21, 2025
ab43ac0
Remediate comment in `static-site/app/whoami/page.tsx` [#1133]
genehack Mar 21, 2025
c4fd97e
Remediate comments in `static-site/components/error-message` [#1133]
genehack Mar 21, 2025
c469f84
Remediate comments in `static-site/components/expandable-tiles` [#1133]
genehack Mar 21, 2025
9c75942
Remediate comments in `static-site/components/flex-center` [#1133]
genehack Mar 21, 2025
1a546f2
Remediate comments in `static-site/components/focus-paragraph` [#1133]
genehack Mar 21, 2025
6bc80e4
Remediate comments and whitespace in `static-site/components/footer` …
genehack Mar 21, 2025
4b0b035
Remediate comment in `static-site/components/line` [#1133]
genehack Mar 21, 2025
1a5640e
Remediate comments in `static-site/components/list-resources` tree [#…
genehack Mar 21, 2025
449bb22
Remediate comments in `static-site/components/logos` [#1133]
genehack Mar 21, 2025
6cf6a41
Remediate comments in `static-site/components/nav` tree [#1133]
genehack Mar 21, 2025
d5225b4
Remediate comments in `static-site/components/people/avatars.tsx` [#1…
genehack Mar 21, 2025
ab2bb17
Remediate comment in `static-site/components/plausible-analytics` [#1…
genehack Mar 21, 2025
14c0f61
Remediate comments in `static-site/comments/spacers` [#1133]
genehack Mar 21, 2025
495b694
Remediate comment in `static-site/components/user-data-wrapper` [#1133]
genehack Mar 21, 2025
94aa4d5
Convert /pathogens over to using splat path to display error message.…
genehack Mar 21, 2025
c8cfb4e
Extract repeated <ErrorBanner> parts of staging and pathogens into co…
genehack Mar 21, 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
28 changes: 25 additions & 3 deletions static-site/app/blog/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,33 +16,51 @@ import styles from "./styles.module.css";

// just to avoid having to repeat this in a couple method sigs...
interface BlogPostParams {
/** a string representing the base name of the post */
id: string;
}

// return a list of params that will get handed to this page at build
// time, to statically build out all the blog posts
/**
* return a list of params that will get handed to this page at build
* time, to statically build out all the blog posts
*/
export function generateStaticParams(): BlogPostParams[] {
return getBlogPosts().map((post) => {
return { id: post.blogUrlName };
});
}

type PopulatedMetadata = Metadata & {
/** the base URL to use for metadata attributes */
metadataBase: URL

/** data to generate OpenGraph headers in the <head> of the page */
openGraph: {
/** a brief description of the post */
description: string

/** an array of image URLs associated with the post */
images: { url: string}[]

/** the name of our site */
siteName: string

/** the title of the blog post */
title: string

/** we have a website! */
type: "website"

/** the URL for the post */
url: URL | string
}
}

// generate opengraph and other metadata tags
/** generate opengraph and other metadata tags */
export async function generateMetadata({
params,
}: {
/** the string identifing the post we're generating metadata for */
params: BlogPostParams;
}): Promise<Metadata> {
const { id } = params;
Expand Down Expand Up @@ -81,9 +99,13 @@ export async function generateMetadata({
return metadata;
}

/**
* A React Server Component for rendering a single blog post
*/
export default async function BlogPost({
params,
}: {
/** the string identifing the post we're generating metadata for */
params: BlogPostParams;
}): Promise<React.ReactElement> {
const { id } = params;
Expand Down
4 changes: 4 additions & 0 deletions static-site/app/blog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { notFound, redirect } from "next/navigation";

import { getBlogPosts } from "./utils";

/**
* A React Server component that handles redirecting requests for
* /blog to the most recent blog post
*/
export default function Index(): void {
const mostRecentPost = getBlogPosts()[0];

Expand Down
16 changes: 15 additions & 1 deletion static-site/app/blog/parseMarkdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ import sanitizeHtml, { Attributes, IOptions, Tag } from "sanitize-html";

import { siteUrl } from "../../data/BaseConfig";

/**
* Render a string of text formatted with Markdown into HTML
*/
export default async function parseMarkdown({
mdString,
addHeadingAnchors = false,
headingAnchorClass = undefined,
}: {
/** the Markdown-formatted text to render */
mdString: string

/** Should `<a>` tags be added to headings? */
Expand Down Expand Up @@ -55,7 +59,17 @@ export default async function parseMarkdown({
return cleanDescription;
}

function transformA(tagName: string, attribs: Attributes): Tag {
/**
* A function to add `target=_blank` and `rel="noreferrer nofollow"`
* attributes to <a> tags that link to external destinations
*/
function transformA(
/** the name of the tag being transformed (will always be `a`) */
tagName: string,

/** attributes of the tag being transformed */
attribs: Attributes
): Tag {
// small helper to keep things dry
const _setAttribs: (attribs: Attributes) => void = (attribs) => {
attribs.target = "_blank";
Expand Down
32 changes: 27 additions & 5 deletions static-site/app/blog/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,33 @@ import { fileURLToPath } from "url";
import parseMarkdown from "./parseMarkdown";

export interface BlogPost {
/** author(s) name(s) */
author: string;

/**
* the "slug" for this post, i.e., the string after `/blog` in the
* post's URL
*/
blogUrlName: string;

/** publication date of the post */
date: string;

/** the Markdown-formatted content of the post */
mdstring: string;

/** the string to use in the blog sidebar for this post */
sidebarTitle: string;

/** the post title */
title: string;
}

// Scans the ./static-site/content/blog directory for .md files and
// returns a chronologically-sorted array of posts, each with some
// basic metadata and the raw (unsanitized) markdown contents.
/**
* Scans the ./static-site/content/blog directory for .md files and
* returns a chronologically-sorted array of posts, each with some
* basic metadata and the raw (unsanitized) markdown contents.
*/
export function getBlogPosts(): BlogPost[] {
const __dirname = path.dirname(fileURLToPath(import.meta.url));

Expand Down Expand Up @@ -82,12 +98,18 @@ export function getBlogPosts(): BlogPost[] {
return blogPosts;
}

/**
* A function to transform a Markdown-formatted string into HTML.
*/
export async function markdownToHtml({
mdString,
headingAnchorClass,
}: {
mdString: string
headingAnchorClass?: string
/** the Markdown-formatted string to transform */
mdString: string;

/** the name to use for the CSS class on heading anchors */
headingAnchorClass?: string;
}): Promise<string> {
try {
return await parseMarkdown({
Expand Down
3 changes: 3 additions & 0 deletions static-site/app/contact/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ export const metadata: Metadata = {
title: "Contact",
};

/**
* A React Server Component that renders the /contact page
*/
export default function ContactPage(): React.ReactElement {
return (
<>
Expand Down
13 changes: 11 additions & 2 deletions static-site/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,20 @@ import "./styles/browserCompatability.css";
import "./styles/bootstrap.css";
import "./styles/globals.css";

// Custom fonts bundled (i.e. no external requests), see
// <https://nextjs.org/docs/pages/building-your-application/optimizing/fonts>
/**
* Custom fonts bundled (i.e. no external requests), see
* <https://nextjs.org/docs/pages/building-your-application/optimizing/fonts>
*/
const lato = Lato({
style: ["normal", "italic"],
subsets: ["latin"],
variable: "--lato",
weight: ["300", "400", "700"],
});

/**
* data used to generate the metadata tags in the <head>
*/
export const metadata: Metadata = {
title: {
absolute: siteTitle,
Expand All @@ -36,6 +41,10 @@ export const metadata: Metadata = {
description: siteTitleAlt,
};

/**
* A React Component that provides the overall page layout used by
* every page on the site.
*/
export default function RootLayout({
children,
}: {
Expand Down
4 changes: 4 additions & 0 deletions static-site/app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import React from "react";

/**
* A React Server Component that renders the 404 page, shown when a
* non-existent URL is requested.
*/
export default function FourOhFour(): React.ReactElement {
return (
<>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { ResourceListingInfo } from "../../components/list-resources/types";
import { ResourceListingInfo } from "../../../components/list-resources/types";

/**
* A callback function used by the <ListResources> component (which
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,40 @@
import React from "react";
import { Metadata } from "next";

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";
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";

import { pathogenResourceListingCallback } from "./callback";

/**
* React Server Component that generates and presents a list of
* pathogen resources.
* React Server Component that generates the content of the /pathogens page
*
* This is abstracted out into a distinct component so that it can
* also be used in the "./not-found.tsx" component, to render the
* /staging page content beneath an error banner, when a bad URL is
* requested.
*/
export default function Pathogens(): React.ReactElement {
export default function PathogensPageContent({
metadata,
}: {
/**
* A Metadata object, that is assumed to have a `title` key with a
* string value
*/
metadata: Metadata;
}): React.ReactElement {
// the cast is not ideal, but it _is_ going to be a string...
const title = metadata.title as string; // eslint-disable-line @typescript-eslint/consistent-type-assertions

return (
<>
<HugeSpacer />
<HugeSpacer />

<h1>Nextstrain-maintained pathogen analyses</h1>
<h1>{title}</h1>

<SmallSpacer />

Expand Down
20 changes: 20 additions & 0 deletions static-site/app/pathogens/[[...pathogens]]/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from "react";

import ErrorBanner from "../../../components/error-banner";

import PathogensPageContent from "./content";
import { metadata } from "./page";

/**
* A React Server component that renders the usual `/staging` page
* content, with an error banner up-top explaining that the requested
* dataset doesn't actuall exist.
*/
export default function FourOhFour(): React.ReactElement {
return (
<>
<ErrorBanner stub="pathogens"/>
<PathogensPageContent metadata={metadata} />
</>
);
}
26 changes: 26 additions & 0 deletions static-site/app/pathogens/[[...pathogens]]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from "react";
import { Metadata } from "next";

import PathogensPageContent from "./content";
import ValidatePathogensUrl from "./validatePathogensUrl";

const title = "Nextstrain-maintained pathogen analyses";

export const metadata: Metadata = {
title,
};

/**
* React Server Component for `/pathogens`
*
* See note in `static-site/app/staging/[[...staging]]/page.tsx`
* to understand how this works
*/
export default function Pathogens(): React.ReactElement {
return (
<>
<ValidatePathogensUrl />
<PathogensPageContent metadata={metadata} />
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"use client";

import { notFound, useParams }from "next/navigation";

/**
* A React Client Component to detect when an invalid `/pathogens/` URL
* is requested, which calls the `notFound()` method to redirect to
* the `not-found.tsx` component
*
* Note that any actually valid `/pathogens/<something>` URL will be
* redirected at the Express router level, before the Next.js router
* is engaged, so if we are trying to render a `/pathogens/<something>`
* URL, it _is_ an error
*/
export default function ValidateStagingUrl(): null {
const params = useParams();

if (params && params["pathogens"]) {
notFound();
}
Comment on lines +18 to +20
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See suggestion to use pathogens/[[...slug]]: #1131 (comment)

else { return null; }

}
5 changes: 3 additions & 2 deletions static-site/app/staging/[[...staging]]/not-found.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React from "react";

import ErrorBanner from "../../../components/error-banner";

import StagingPageContent from "./content";
import ErrorBanner from "./error-banner";
import { metadata } from "./page";

/**
Expand All @@ -12,7 +13,7 @@ import { metadata } from "./page";
export default function FourOhFour(): React.ReactElement {
return (
<>
<ErrorBanner />
<ErrorBanner stub="staging" />
<StagingPageContent metadata={metadata} />
</>
);
Expand Down
3 changes: 3 additions & 0 deletions static-site/app/team/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export const metadata: Metadata = {
title: "Team",
};

/**
* A React Server Component that renders the /team page
*/
export default function TeamPage(): React.ReactElement {
return (
<div className={styles.teamPage}>
Expand Down
3 changes: 3 additions & 0 deletions static-site/app/whoami/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import styles from "./styles.module.css";
// components aren't allowed to do that, and we need to be a client
// component to get access to the user context

/**
* A React Client Component that renders the /whoami page
*/
export default function WhoAmI(): React.ReactElement {
const { user, groupMemberships } = useContext(UserContext);

Expand Down
Loading