+ className?: string
+}
+
+// Styles
const Content = styled.div`
margin-bottom: 1.45rem;
@@ -22,7 +27,7 @@ const Content = styled.div`
position: absolute;
top: -2px; /* adjusts circle + number up and down */
left: -3rem;
- width: ${({ size }) => (size ? size : "35px")};
+ width: 35px;
aspect-ratio: 1;
height: 2rem;
padding-top: 7px; /* adjusts number up and down */
@@ -33,9 +38,9 @@ const Content = styled.div`
}
`
-// listData should be a list of strings, or HTML components
+// `listData` should be a list of strings, or HTML components
// ex: [string
] or ['string']
-const OrderedList = ({ listData, className }) => {
+const OrderedList: React.FC = ({ listData, className }) => {
return (
diff --git a/src/components/PageMetadata.js b/src/components/PageMetadata.tsx
similarity index 85%
rename from src/components/PageMetadata.js
rename to src/components/PageMetadata.tsx
index 59ffac9f94a..18a9f038de3 100644
--- a/src/components/PageMetadata.js
+++ b/src/components/PageMetadata.tsx
@@ -2,16 +2,41 @@ import React from "react"
import PropTypes from "prop-types"
import { Helmet } from "react-helmet"
import { useStaticQuery, graphql } from "gatsby"
-import { useIntl } from "gatsby-plugin-intl"
+import { useIntl } from "react-intl"
import { Location } from "@reach/router"
import { getSrc } from "gatsby-plugin-image"
-import { languageMetadata } from "../utils/languages"
+import { isLang } from "../utils/languages"
import { translateMessageId } from "../utils/translations"
-const supportedLanguages = Object.keys(languageMetadata)
+type NameMeta = {
+ name: string
+ content: string
+}
+
+type PropMeta = {
+ property: string
+ content: string
+}
+
+export type Meta = NameMeta | PropMeta
+
+export interface IProps {
+ description?: string | null
+ meta?: Array
+ image?: string
+ title: string
+ canonicalUrl?: string | null
+}
-const PageMetadata = ({ description, meta, title, image, canonicalUrl }) => {
+const PageMetadata: React.FC = ({
+ description,
+ meta = [],
+ title,
+ image,
+ canonicalUrl,
+}) => {
+ if (!description) console.warn(`Missing PageMetadata description: ${title}`)
const {
site,
ogImageDefault,
@@ -87,7 +112,7 @@ const PageMetadata = ({ description, meta, title, image, canonicalUrl }) => {
const { pathname } = location
let canonicalPath = pathname
const firstDirectory = canonicalPath.split("/")[1]
- if (!supportedLanguages.includes(firstDirectory)) {
+ if (!isLang(firstDirectory)) {
canonicalPath = `/en${pathname}`
}
const canonical =
@@ -207,7 +232,18 @@ PageMetadata.defaultProps = {
PageMetadata.propTypes = {
description: PropTypes.string,
- meta: PropTypes.arrayOf(PropTypes.object),
+ meta: PropTypes.arrayOf(
+ PropTypes.oneOfType([
+ PropTypes.shape({
+ name: PropTypes.string.isRequired,
+ content: PropTypes.string.isRequired,
+ }),
+ PropTypes.shape({
+ property: PropTypes.string.isRequired,
+ content: PropTypes.string.isRequired,
+ }),
+ ]).isRequired
+ ),
image: PropTypes.string,
title: PropTypes.string.isRequired,
}
diff --git a/src/components/Pill.js b/src/components/Pill.tsx
similarity index 80%
rename from src/components/Pill.js
rename to src/components/Pill.tsx
index ba41d61cf51..68ecc23fe6f 100644
--- a/src/components/Pill.js
+++ b/src/components/Pill.tsx
@@ -1,7 +1,7 @@
import React from "react"
import styled from "styled-components"
-const Primary = styled.div`
+const Primary = styled.div<{ color?: string }>`
display: flex;
background: ${(props) =>
props.color
@@ -27,8 +27,18 @@ const Secondary = styled.div`
font-size: 0.75rem;
border-radius: 0.25rem;
`
+export interface IProps {
+ className?: string
+ isSecondary?: boolean
+ color?: string
+}
-const Pill = ({ children, className, isSecondary, color }) => {
+const Pill: React.FC = ({
+ children,
+ className,
+ isSecondary,
+ color,
+}) => {
return isSecondary ? (
{children}
) : (
diff --git a/src/components/PreMergeBanner.tsx b/src/components/PreMergeBanner.tsx
index d9c5c45adda..41afc6b0021 100644
--- a/src/components/PreMergeBanner.tsx
+++ b/src/components/PreMergeBanner.tsx
@@ -2,25 +2,44 @@ import React from "react"
import styled from "styled-components"
import BannerNotification from "./BannerNotification"
import Link from "./Link"
+import Translation from "./Translation"
const StyledBannerNotification = styled(BannerNotification)`
- display: block;
+ display: flex;
z-index: 1;
+ justify-content: center;
+ p {
+ max-width: 100ch;
+ margin: 0;
+ padding: 0;
+ }
a {
text-decoration: underline;
}
+ text-align: center;
`
export interface IProps {
+ announcementOnly?: boolean
className?: string
}
-const PreMergeBanner: React.FC = ({ className }) => (
+const PreMergeBanner: React.FC = ({
+ announcementOnly = false,
+ className,
+ children,
+}) => (
- The Merge is approaching, and with it comes many changes to Ethereum. Some
- content on this page may be out-of-date related to these changes, and
- updates are coming soon.{" "}
- Learn more about The Merge.
+
+ {" "}
+ {!announcementOnly && (
+
+ )}{" "}
+ {children}{" "}
+
+
+
+
)
diff --git a/src/components/ProductCard.js b/src/components/ProductCard.tsx
similarity index 87%
rename from src/components/ProductCard.js
rename to src/components/ProductCard.tsx
index a126dcef53c..853bb48b3ff 100644
--- a/src/components/ProductCard.js
+++ b/src/components/ProductCard.tsx
@@ -6,7 +6,9 @@ import { useQuery, gql } from "@apollo/client"
import GitStars from "./GitStars"
import ButtonLink from "./ButtonLink"
-const ImageWrapper = styled.div`
+const ImageWrapper = styled.div<{
+ background: string
+}>`
display: flex;
flex-direction: row;
justify-content: center;
@@ -50,7 +52,9 @@ const Content = styled.div`
flex-direction: column;
`
-const Title = styled.h3`
+const Title = styled.h3<{
+ gitHidden: boolean
+}>`
margin-top: ${(props) => (props.gitHidden ? "2rem" : "3rem")};
margin-bottom: 0.75rem;
`
@@ -67,7 +71,9 @@ const SubjectContainer = styled.div`
padding: 0 1.5rem;
`
-const SubjectPill = styled.div`
+const SubjectPill = styled.div<{
+ subject: string
+}>`
text-align: center;
padding: 0 0.5rem;
margin: 0 0.75rem 0.5rem 0;
@@ -133,7 +139,21 @@ const REPO_DATA = gql`
}
`
-const ProductCard = ({
+export interface IProps {
+ url: string
+ background: string
+ image: string
+ name: string
+ description?: string
+ note?: string
+ alt?: string
+ githubUrl?: string
+ repoLangCount?: number
+ subjects?: Array
+ hideStars?: boolean
+}
+
+const ProductCard: React.FC = ({
url,
background,
image,
@@ -199,11 +219,13 @@ const ProductCard = ({
))}
{hasRepoData &&
- data.repository.languages.nodes.map(({ name }, idx) => (
-
- {name.toUpperCase()}
-
- ))}
+ data.repository.languages.nodes.map(
+ ({ name }: { name: string }, idx: number) => (
+
+ {name.toUpperCase()}
+
+ )
+ )}
Open {name}
diff --git a/src/components/ProductList.js b/src/components/ProductList.tsx
similarity index 89%
rename from src/components/ProductList.js
rename to src/components/ProductList.tsx
index c6601eabd62..8ad532bc395 100644
--- a/src/components/ProductList.js
+++ b/src/components/ProductList.tsx
@@ -72,7 +72,21 @@ const StyledButton = styled(ButtonLink)`
}
`
-const ProductList = ({ content, category }) => (
+export interface Content {
+ title: string
+ description: string
+ link?: string
+ image?: any
+ alt: string
+ id?: string
+}
+
+export interface IProps {
+ content: Array
+ category: string
+}
+
+const ProductList: React.FC = ({ content, category }) => (
{category}
{content.map(({ title, description, link, image, alt, id }, idx) => (
diff --git a/src/components/RandomAppList.js b/src/components/RandomAppList.tsx
similarity index 81%
rename from src/components/RandomAppList.js
rename to src/components/RandomAppList.tsx
index 43974db8d3f..97f1703ffd2 100644
--- a/src/components/RandomAppList.js
+++ b/src/components/RandomAppList.tsx
@@ -3,8 +3,15 @@ import { shuffle } from "lodash"
import Link from "./Link"
import Translation from "./Translation"
+import { TranslationKey } from "../utils/translations"
-const appList = [
+interface App {
+ name: string
+ url: string
+ description: TranslationKey
+}
+
+const appList: Array = [
{
name: "Gitcoin",
url: "https://gitcoin.co",
@@ -42,8 +49,10 @@ const appList = [
},
]
-const RandomAppList = () => {
- const [randomAppList, setRandomAppList] = useState([])
+export interface IProps {}
+
+const RandomAppList: React.FC = () => {
+ const [randomAppList, setRandomAppList] = useState>([])
useEffect(() => {
const list = shuffle(appList)
diff --git a/src/components/ReleaseBanner.js b/src/components/ReleaseBanner.tsx
similarity index 74%
rename from src/components/ReleaseBanner.js
rename to src/components/ReleaseBanner.tsx
index 36a05aae88b..d36be1dae93 100644
--- a/src/components/ReleaseBanner.js
+++ b/src/components/ReleaseBanner.tsx
@@ -12,6 +12,7 @@ import Translation from "./Translation"
// Utils
import { getFreshData } from "../utils/cache"
+import { TranslationKey } from "../utils/translations"
// Constants
import { GATSBY_FUNCTIONS_PATH } from "../constants"
@@ -48,16 +49,28 @@ const Span = styled.span`
padding-left: 5px;
`
-const ReleaseBanner = ({ storageKey }) => {
- const [loading, setLoading] = useState(true)
- const [show, setShow] = useState(true)
- const [timeLeft, setTimeLeft] = useState("0")
+interface CountdownRendererProps {
+ days: number
+ hours: number
+ minutes: number
+ seconds: number
+ completed: boolean
+}
+
+export interface IProps {
+ storageKey: string
+}
+
+const ReleaseBanner: React.FC = ({ storageKey }) => {
+ const [loading, setLoading] = useState(true)
+ const [show, setShow] = useState(true)
+ const [timeLeft, setTimeLeft] = useState("0")
useEffect(() => {
setLoading(true)
- const fetchBlockInfo = async () => {
+ const fetchBlockInfo = async (): Promise => {
try {
- const data = await getFreshData(
+ const data: string = await getFreshData(
`${GATSBY_FUNCTIONS_PATH}/etherscanBlock`
)
setTimeLeft(data)
@@ -77,14 +90,20 @@ const ReleaseBanner = ({ storageKey }) => {
}
}, [storageKey])
- const handleClose = () => {
+ const handleClose = (): void => {
if (localStorage) {
- localStorage.setItem(storageKey, true)
+ localStorage.setItem(storageKey, "true")
}
setShow(false)
}
- const renderer = ({ days, hours, minutes, seconds, completed }) => {
+ const renderer = ({
+ days,
+ hours,
+ minutes,
+ seconds,
+ completed,
+ }: CountdownRendererProps): React.ReactNode => {
if (completed) {
return (
@@ -92,7 +111,9 @@ const ReleaseBanner = ({ storageKey }) => {
-
+
@@ -107,7 +128,7 @@ const ReleaseBanner = ({ storageKey }) => {
-
+
{zeroPad(days, 2)}:{zeroPad(hours, 2)}:{zeroPad(minutes, 2)}:
{zeroPad(seconds, 2)}!
diff --git a/src/components/Roadmap.js b/src/components/Roadmap.tsx
similarity index 84%
rename from src/components/Roadmap.js
rename to src/components/Roadmap.tsx
index 235cc5433ef..2762de73657 100644
--- a/src/components/Roadmap.js
+++ b/src/components/Roadmap.tsx
@@ -1,9 +1,9 @@
import React, { useState, useEffect } from "react"
-import { useIntl } from "gatsby-plugin-intl"
+import { useIntl } from "react-intl"
import styled from "styled-components"
import axios from "axios"
-import Translation from "../components/Translation"
+import Translation from "./Translation"
import Link from "./Link"
import { FakeLinkExternal, CardItem as Item } from "./SharedStyledComponents"
@@ -23,7 +23,27 @@ const ErrorMsg = styled.div`
color: ${(props) => props.theme.colors.fail};
`
-const IssueSection = ({ issues }) => {
+export interface Label {
+ name: string
+}
+
+export interface Issue {
+ html_url?: string
+ title: string
+ errorMsg?: string
+ labels: Array