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

Update 404 page to the new design #3948

Merged
merged 2 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
2 changes: 1 addition & 1 deletion baker/siteRenderers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ChartsIndexPage, ChartIndexItem } from "../site/ChartsIndexPage.js"
import { DynamicCollectionPage } from "../site/collections/DynamicCollectionPage.js"
import { StaticCollectionPage } from "../site/collections/StaticCollectionPage.js"
import { SearchPage } from "../site/search/SearchPage.js"
import { NotFoundPage } from "../site/NotFoundPage.js"
import NotFoundPage from "../site/NotFoundPage.js"
import { DonatePage } from "../site/DonatePage.js"
import { ThankYouPage } from "../site/ThankYouPage.js"
import OwidGdocPage from "../site/gdocs/OwidGdocPage.js"
Expand Down
82 changes: 52 additions & 30 deletions site/NotFoundPage.tsx
Original file line number Diff line number Diff line change
@@ -1,48 +1,39 @@
import React from "react"
import * as React from "react"
import { Head } from "./Head.js"
import { SiteHeader } from "./SiteHeader.js"
import { SiteFooter } from "./SiteFooter.js"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome/index.js"
import { faSearch } from "@fortawesome/free-solid-svg-icons"
import { Html } from "./Html.js"
import NotFoundPageForm from "./NotFoundPageForm.js"

export const NotFoundPage = (props: { baseUrl: string }) => {
export default function NotFoundPage({ baseUrl }: { baseUrl: string }) {
return (
<Html>
<Head
canonicalUrl={`${props.baseUrl}/search`}
canonicalUrl={`${baseUrl}/search`}
pageTitle="404 Not Found"
pageDesc="Search articles and charts on Our World in Data."
baseUrl={props.baseUrl}
baseUrl={baseUrl}
/>
<body className="NotFoundPage">
<SiteHeader baseUrl={props.baseUrl} />
<SiteHeader baseUrl={baseUrl} />
<main>
<h1>Sorry, that page doesn’t exist!</h1>
<p>
You can search below or{" "}
<a href="/">return to the homepage</a>.
<NotFoundPageIcon />
<h1 className="NotFoundPage__heading subtitle-1">
Sorry, that page doesn’t exist!
</h1>
<p className="body-2-semibold">
You may have followed an outdated link or have mistyped
the URL.
</p>
<form action="/search" method="GET">
<div className="inputWrapper">
<input
id="search_q"
type="search"
name="q"
autoFocus
/>
<FontAwesomeIcon icon={faSearch} />
</div>
<button
aria-label="Submit search"
className="btn"
type="submit"
>
Search
</button>
</form>
<p className="body-2-semibold">
You can search for what you were hoping to find below or{" "}
<a href="/">visit our homepage</a>.
Copy link
Member

Choose a reason for hiding this comment

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

This link is missing a hover style - is it meant to be owid-link-90 ? (we should probably add a util class for that mixin, like we do for typography)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Almost, it should be blue-60 color. I'll still use the mixin.

</p>
<div id="not-found-page-form">
<NotFoundPageForm />
</div>
</main>
<SiteFooter hideDonate={true} baseUrl={props.baseUrl} />
<SiteFooter hideDonate={true} baseUrl={baseUrl} />
<script
type="module"
dangerouslySetInnerHTML={{
Expand All @@ -55,3 +46,34 @@ export const NotFoundPage = (props: { baseUrl: string }) => {
</Html>
)
}

function NotFoundPageIcon() {
return (
<svg
className="NotFoundPageIcon"
width="194"
height="194"
viewBox="0 0 194 194"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="194" height="194" rx="97" fill="#EBEEF2" />
<path
d="M129.439 65.08H64.6391C61.519 65.08 59.0391 67.6399 59.0391 70.68V123.32C59.0391 126.44 61.5989 128.92 64.6391 128.92H129.359C132.479 128.92 134.959 126.36 134.959 123.32V70.68C135.039 67.56 132.479 65.08 129.439 65.08ZM83.0391 70.8402C84.3989 70.8402 85.4391 71.9602 85.4391 73.2402C85.4391 74.6 84.319 75.6401 83.0391 75.6401C81.6792 75.6401 80.6391 74.5201 80.6391 73.2402C80.6391 71.88 81.6792 70.8402 83.0391 70.8402ZM75.6791 70.8402C77.0389 70.8402 78.0791 71.9602 78.0791 73.2402C78.0791 74.6 76.959 75.6401 75.6791 75.6401C74.3192 75.6401 73.2791 74.5201 73.2791 73.2402C73.2791 71.88 74.3989 70.8402 75.6791 70.8402ZM68.3191 70.8402C69.6789 70.8402 70.7191 71.9602 70.7191 73.2402C70.7191 74.6 69.599 75.6401 68.3191 75.6401C66.9592 75.6401 65.9191 74.5201 65.9191 73.2402C65.9191 71.88 67.0391 70.8402 68.3191 70.8402ZM127.759 121.24C127.759 121.48 127.599 121.64 127.359 121.64H66.7191C66.479 121.64 66.3191 121.48 66.3191 121.24V81.4802H127.759V121.24Z"
fill="#6E87A2"
/>
<path
d="M74.36 106.004H80.5199V110.386C80.5199 110.785 80.8399 111.183 81.3199 111.183H83.7998C84.1998 111.183 84.5998 110.864 84.5998 110.386V106.004H86.8399C87.2399 106.004 87.6399 105.685 87.6399 105.207V102.737C87.6399 102.339 87.3199 101.94 86.8399 101.94H84.5998V98.6733C84.5998 98.2749 84.2798 97.8765 83.7998 97.8765H81.3199C80.9199 97.8765 80.5199 98.1953 80.5199 98.6733V101.94H77.8797L78.6797 92.8566C78.6797 92.4582 78.3597 92.0598 77.9597 91.9801L75.5597 91.741C75.1597 91.741 74.7597 92.0598 74.6797 92.4582L73.6396 105.048C73.5599 105.685 73.88 106.004 74.3599 106.004L74.36 106.004Z"
fill="#6E87A2"
/>
<path
d="M106.28 106.004H112.44V110.386C112.44 110.785 112.76 111.183 113.24 111.183H115.72C116.12 111.183 116.52 110.864 116.52 110.386V106.004H118.76C119.16 106.004 119.56 105.685 119.56 105.207V102.737C119.56 102.339 119.24 101.94 118.76 101.94H116.52V98.6733C116.52 98.2749 116.2 97.8765 115.72 97.8765H113.24C112.84 97.8765 112.44 98.1953 112.44 98.6733V101.94L109.8 101.94L110.6 92.8563C110.6 92.4579 110.28 92.0595 109.88 91.9799L107.48 91.7407C107.08 91.7407 106.68 92.0595 106.6 92.4579L105.56 105.047C105.48 105.685 105.88 106.004 106.28 106.004L106.28 106.004Z"
fill="#6E87A2"
/>
<path
d="M90.9201 111.183H101.8C102.2 111.183 102.6 110.864 102.6 110.386V92.7768C102.6 92.3784 102.28 91.98 101.8 91.98H90.9201C90.5201 91.98 90.1201 92.2988 90.1201 92.7768V110.386C90.1201 110.864 90.5201 111.183 90.9201 111.183V111.183ZM94.1201 95.964H98.5201V107.199H94.1201V95.964Z"
fill="#6E87A2"
/>
</svg>
)
}
49 changes: 49 additions & 0 deletions site/NotFoundPageForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import * as React from "react"
import { useState, useRef, useEffect } from "react"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faSearch, faCircleXmark } from "@fortawesome/free-solid-svg-icons"

export default function NotFoundPageForm() {
const [searchQuery, setSearchQuery] = useState("")
const inputRef = useRef<HTMLInputElement>(null)

useEffect(() => {
// Browser keeps the DOM input value on page refresh.
const domValue = inputRef.current?.value
if (domValue) setSearchQuery(domValue)
}, [])

return (
<form className="NotFoundPageForm" action="/search" method="GET">
<input
id="search_q"
className="NotFoundPageForm__input"
ref={inputRef}
type="search"
placeholder="Search..."
name="q"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
required
autoFocus
/>
{searchQuery && (
<button
className="NotFoundPageForm__button NotFoundPageForm__reset-button"
type="reset"
title="Clear search"
onClick={() => setSearchQuery("")}
>
<FontAwesomeIcon icon={faCircleXmark} />
</button>
)}
<button
className="NotFoundPageForm__button NotFoundPageForm__submit-button"
type="submit"
title="Search"
>
<FontAwesomeIcon icon={faSearch} />
</button>
</form>
)
}
14 changes: 7 additions & 7 deletions site/NotFoundPageMain.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import * as React from "react"
import { hydrate } from "react-dom"
import NotFoundPageForm from "./NotFoundPageForm.js"
import { SiteAnalytics } from "./SiteAnalytics.js"

const analytics = new SiteAnalytics()

export function runNotFoundPage() {
const query = window.location.pathname.split("/")
const searchInput = document.getElementById("search_q") as HTMLInputElement
if (searchInput && query.length)
searchInput.value = decodeURIComponent(query[query.length - 1]).replace(
/[\-_\+|]/g,
" "
)
analytics.logPageNotFoundError(window.location.href)
hydrate(
<NotFoundPageForm />,
document.getElementById("not-found-page-form")
)
}
129 changes: 79 additions & 50 deletions site/owid.scss
Original file line number Diff line number Diff line change
Expand Up @@ -793,71 +793,100 @@ a.ref sup {
}
}

.NotFoundPage > main {
> form {
@include content-wrapper;
max-width: $wrapper-max-width;
margin-top: 20px;
.NotFoundPage {
color: $blue-60;
background-color: $gray-10;

main {
display: flex;
margin-bottom: 30px;
}
flex-direction: column;
justify-content: center;
text-align: center;
min-height: 600px;
padding: 0 16px;

> form > .inputWrapper {
width: 100%;
position: relative;
padding-right: 5px;
}
p {
margin: 0;
}

> form > .inputWrapper input {
width: 100%;
height: 40px;
padding-left: 35px;
a {
color: inherit;
text-decoration: underline;
}
}
}

> form > .inputWrapper svg {
position: absolute;
left: 10px;
top: 12px;
}
.NotFoundPage__heading {
color: $blue-90;
}

> form > button {
width: 160px;
height: 40px;
margin: 0px;
position: relative;
align-items: center;
justify-content: center;
padding-top: 0px;
padding-bottom: 0px;
padding-left: 16px;
padding-right: 16px;
border: none;
border-radius: 4px;
cursor: pointer;
.NotFoundPageForm {
position: relative;
width: 100%;
max-width: 412px;
margin: 32px auto;
}

.NotFoundPageForm__input {
@include body-3-medium;
color: $blue-90;
width: 100%;
height: 48px;
padding: 0 16px;
border: 1px solid $blue-20;
padding-right: 48px;

&:focus {
outline: none;
text-decoration: none;
box-sizing: border-box;
touch-action: manipulation;
background: #1865f2;
color: #ffffff;
border: 1px solid $blue-40;
}

&::placeholder {
color: $blue-50;
}
}

.NotFoundPage {
main {
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
.NotFoundPageForm__button {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: 0;
width: 48px;
height: 100%;
color: $blue-50;
border: none;
background: none;
padding: 0;
cursor: pointer;

&:hover {
color: $blue-90;
}
}

footer {
// Hide the header, we need to include it for scripts but don't want to
// show it because it does not go to the bottom of the page and looks weird
display: none;
.NotFoundPageForm__reset-button {
right: 48px;

&::after {
content: "";
display: block;
position: absolute;
right: 0;
height: 26px;
width: 1px;
background-color: $blue-20;
}
}

.NotFoundPageForm__submit-button {
right: 0;
}

.NotFoundPageIcon {
margin: 0 auto;
}

.variantName {
margin-left: 3px;
color: $grey-text-color;
Expand Down