diff --git a/.storybook/i18next.ts b/.storybook/i18next.ts index 4f1030040fc..5f8c98f4116 100644 --- a/.storybook/i18next.ts +++ b/.storybook/i18next.ts @@ -1,6 +1,5 @@ import i18n, { Resource } from "i18next" import { initReactI18next } from "gatsby-plugin-react-i18next" -import fs from "fs" export const baseLocales = { en: { title: "English", left: "En" }, @@ -15,6 +14,7 @@ const ns = [ "glossary", "page-about", "page-index", + "page-learn", "page-upgrades", "page-developers-index", ] diff --git a/.storybook/main.ts b/.storybook/main.ts index 9f3bb88a066..b8c350a0fce 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -13,7 +13,7 @@ const config: StorybookConfig = { "@chakra-ui/storybook-addon", "storybook-react-i18next", ], - staticDirs: ["../static"], + staticDirs: ["../static", "../src/assets"], babel: async () => ({ ...babelConfig, }), diff --git a/src/assets/heroes/learn-hub-hero.png b/src/assets/heroes/learn-hub-hero.png new file mode 100644 index 00000000000..41b03361cda Binary files /dev/null and b/src/assets/heroes/learn-hub-hero.png differ diff --git a/src/assets/mainnet.png b/src/assets/mainnet.png new file mode 100644 index 00000000000..c8290ba2271 Binary files /dev/null and b/src/assets/mainnet.png differ diff --git a/src/components/Hero/CallToAction.tsx b/src/components/Hero/CallToAction.tsx new file mode 100644 index 00000000000..17aa71a76d4 --- /dev/null +++ b/src/components/Hero/CallToAction.tsx @@ -0,0 +1,26 @@ +import * as React from "react" +import { MatomoEventOptions, trackCustomEvent } from "../../utils/matomo" +import { Button, type IButtonProps } from "../Buttons" + +export type CallToActionProps = Omit & { + content: React.ReactNode + matomo: MatomoEventOptions +} + +export function CallToAction(props: CallToActionProps & { index: React.Key }) { + const { content, matomo, index, ...rest } = props + + const handleClick = () => trackCustomEvent(matomo) + + const buttonProps: IButtonProps = { + variant: index === 0 ? "solid" : "outline", + isSecondary: index !== 0, + flex: { base: 1, md: "initial" }, + } + + return ( + + ) +} diff --git a/src/components/Hero/ContentHero/ContentHero.stories.tsx b/src/components/Hero/ContentHero/ContentHero.stories.tsx new file mode 100644 index 00000000000..8554a32807f --- /dev/null +++ b/src/components/Hero/ContentHero/ContentHero.stories.tsx @@ -0,0 +1,79 @@ +import * as React from "react" +import { Meta, StoryObj } from "@storybook/react" +import ContentHeroComponent, { ContentHeroProps } from "." +import { IGatsbyImageData } from "gatsby-plugin-image" +import { useTranslation } from "gatsby-plugin-react-i18next" + +type ContentHeroType = typeof ContentHeroComponent + +const meta = { + title: "Organisms / Layouts / Hero", + component: ContentHeroComponent, + parameters: { + layout: "none", + }, + argTypes: { + heroImgSrc: { + table: { + disable: true, + }, + }, + }, +} satisfies Meta + +export default meta + +// Comes from the original compiled querying +const mockGatsbyImgData: IGatsbyImageData = { + layout: "constrained", + images: { + fallback: { + src: "/mainnet.png", + sizes: "100vw", + }, + sources: [ + { + srcSet: "/mainnet.png", + type: "image/webp", + sizes: "100vw", + }, + ], + }, + width: 1, + height: 1, +} + +export const ContentHero: StoryObj = { + render: () => { + const { t } = useTranslation() + + const buttons: ContentHeroProps["buttons"] = [ + { + content: t("hero-button-lets-get-started"), + toId: "what-is-crypto-ethereum", + matomo: { + eventCategory: "learn hub hero buttons", + eventAction: "click", + eventName: "lets get started", + }, + }, + { + content: "Button", + matomo: { + eventCategory: "learn hub hero buttons", + eventAction: "click", + eventName: "lets get started", + }, + }, + ] + return ( + + ) + }, +} diff --git a/src/components/Hero/ContentHero/index.tsx b/src/components/Hero/ContentHero/index.tsx new file mode 100644 index 00000000000..d5c975e4214 --- /dev/null +++ b/src/components/Hero/ContentHero/index.tsx @@ -0,0 +1,55 @@ +import * as React from "react" +import { Box, Heading, HStack, SimpleGrid, Stack, Text } from "@chakra-ui/react" +import Breadcrumbs, { IProps as BreadcrumbsProps } from "../../Breadcrumbs" +import GatsbyImage from "../../GatsbyImage" +import { CallToAction } from "../CallToAction" +import { CommonHeroProps } from "../utils" + +export interface ContentHeroProps extends Omit { + breadcrumbs: BreadcrumbsProps +} + +const ContentHero = (props: ContentHeroProps) => { + const { breadcrumbs, heroImgSrc, buttons, title, description } = props + return ( + + + + + + + + + + {title} + + {description} + + {buttons + ? buttons.map((button, idx) => { + if (!button) return + + return + }) + : null} + + + {/* TODO: + * Add conditional Big Stat box here + */} + + + + ) +} + +export default ContentHero diff --git a/src/components/Hero/HomeHero/HomeHero.stories.tsx b/src/components/Hero/HomeHero/HomeHero.stories.tsx new file mode 100644 index 00000000000..12b8f012f56 --- /dev/null +++ b/src/components/Hero/HomeHero/HomeHero.stories.tsx @@ -0,0 +1,50 @@ +import * as React from "react" +import { Meta, StoryObj } from "@storybook/react" +import HomeHeroComponent from "." +import { IGatsbyImageData } from "gatsby-plugin-image" + +type HomeHeroType = typeof HomeHeroComponent + +const meta = { + title: "Organisms / Layouts / Hero", + component: HomeHeroComponent, + parameters: { + layout: "none", + }, + argTypes: { + heroImgSrc: { + table: { + disable: true, + }, + }, + }, +} satisfies Meta + +export default meta + +// Comes from the original compiled querying +const mockGatsbyImgData: IGatsbyImageData = { + layout: "fullWidth", + images: { + fallback: { + src: "/home/hero.png", + sizes: "100vw", + }, + sources: [ + { + srcSet: "/home/hero.png", + type: "image/webp", + sizes: "100vw", + }, + ], + }, + width: 1, + height: 1, +} + +export const HomeHero: StoryObj = { + args: { + heroImgSrc: mockGatsbyImgData, + }, + render: (args) => , +} diff --git a/src/components/Hero/HomeHero/index.tsx b/src/components/Hero/HomeHero/index.tsx new file mode 100644 index 00000000000..ffd25565ddd --- /dev/null +++ b/src/components/Hero/HomeHero/index.tsx @@ -0,0 +1,49 @@ +import * as React from "react" +import { useTranslation } from "react-i18next" +import { Box, Heading, Stack, Text, VStack } from "@chakra-ui/react" +import { ButtonLink } from "../../Buttons" +import Morpher from "../../Morpher" +import Translation from "../../Translation" +import GatsbyImage from "../../GatsbyImage" +import { CommonHeroProps } from "../utils" + +export interface HomeHeroProps extends Pick {} + +const HomeHero = ({ heroImgSrc }: HomeHeroProps) => { + const { t } = useTranslation() + return ( + + + + + + + + + + + + + + + + + + + + ) +} + +export default HomeHero diff --git a/src/components/Hero/HubHero/HubHero.stories.tsx b/src/components/Hero/HubHero/HubHero.stories.tsx new file mode 100644 index 00000000000..2a87854b78f --- /dev/null +++ b/src/components/Hero/HubHero/HubHero.stories.tsx @@ -0,0 +1,87 @@ +import * as React from "react" +import { Meta, StoryObj } from "@storybook/react" +import HubHeroComponent, { HubHeroProps } from "./" +import { Box } from "@chakra-ui/react" +import { IGatsbyImageData } from "gatsby-plugin-image" +import { useTranslation } from "react-i18next" + +type HubHeroType = typeof HubHeroComponent + +const meta = { + title: "Organisms / Layouts / Hero", + component: HubHeroComponent, + parameters: { + layout: "none", + }, + decorators: [ + (Story) => ( + + + + ), + ], +} satisfies Meta + +export default meta + +const mockGatsbyImgData: IGatsbyImageData = { + layout: "fullWidth", + images: { + fallback: { + src: "/heroes/learn-hub-hero.png", + sizes: "100vw", + }, + sources: [ + { + srcSet: "/heroes/learn-hub-hero.png", + type: "image/webp", + sizes: "100vw", + }, + ], + }, + width: 1, + height: 1, +} + +export const HubHero: StoryObj = { + args: { + title: "learn-hub", + header: "hero-header", + description: "hero-subtitle", + heroImgSrc: mockGatsbyImgData, + }, + render: (args) => { + const { t } = useTranslation() + const { title, header, description, ...rest } = args + + const buttons: HubHeroProps["buttons"] = [ + { + content: t("hero-button-lets-get-started"), + toId: "what-is-crypto-ethereum", + matomo: { + eventCategory: "learn hub hero buttons", + eventAction: "click", + eventName: "lets get started", + }, + }, + { + content: "Button", + matomo: { + eventCategory: "learn hub hero buttons", + eventAction: "click", + eventName: "lets get started", + }, + }, + ] + + return ( + + ) + }, +} diff --git a/src/components/Hero/HubHero/index.tsx b/src/components/Hero/HubHero/index.tsx new file mode 100644 index 00000000000..b51c690074a --- /dev/null +++ b/src/components/Hero/HubHero/index.tsx @@ -0,0 +1,89 @@ +import * as React from "react" +import { + Box, + Heading, + HStack, + Stack, + Text, + useColorModeValue, +} from "@chakra-ui/react" +import GatsbyImage from "../../GatsbyImage" +import { CallToAction } from "../CallToAction" +import { CommonHeroProps } from "../utils" + +export interface HubHeroProps extends CommonHeroProps {} + +const HubHero = (props: HubHeroProps) => { + const { heroImgSrc, title, header, description, buttons } = props + + if (buttons && buttons.length > 2) { + throw Error( + "Can not have more than two call-to-action buttons in this hero component." + ) + } + + const largeContentBg = useColorModeValue( + "rgba(255, 255, 255, 0.80)", + "rgba(34, 34, 34, 0.80)" + ) + + return ( + + + + + {title} + + + {header} + {description} + + + {buttons + ? buttons.map((button, idx) => { + if (!button) return + return + }) + : null} + + + + ) +} + +export default HubHero diff --git a/src/components/Hero/MdxHero/MdxHero.stories.tsx b/src/components/Hero/MdxHero/MdxHero.stories.tsx new file mode 100644 index 00000000000..bdaa1d54538 --- /dev/null +++ b/src/components/Hero/MdxHero/MdxHero.stories.tsx @@ -0,0 +1,30 @@ +import * as React from "react" +import { Meta, StoryObj } from "@storybook/react" +import MdxHeroComponent from "./" +import { HStack } from "@chakra-ui/react" + +type MdxHeroType = typeof MdxHeroComponent + +const meta = { + title: "Organisms / Layouts / Hero", + parameters: { + layout: "none", + }, + decorators: [ + (Story) => ( + + + + ), + ], +} satisfies Meta + +export default meta + +export const MdxHero: StoryObj = { + args: { + breadcrumbs: { slug: "/en/staking/solo/" }, + title: "Solo stake your Eth", + }, + render: (args) => , +} diff --git a/src/components/Hero/MdxHero/index.tsx b/src/components/Hero/MdxHero/index.tsx new file mode 100644 index 00000000000..40fadf700bb --- /dev/null +++ b/src/components/Hero/MdxHero/index.tsx @@ -0,0 +1,22 @@ +import { Heading, Stack } from "@chakra-ui/react" +import * as React from "react" +import Breadcrumbs, { IProps as BreadcrumbsProps } from "../../Breadcrumbs" +import { CommonHeroProps } from "../utils" + +export interface MdxHeroProps extends Pick { + breadcrumbs: BreadcrumbsProps +} + +const MdxHero = (props: MdxHeroProps) => { + const { breadcrumbs, title } = props + return ( + + + + {title} + + + ) +} + +export default MdxHero diff --git a/src/components/Hero/index.ts b/src/components/Hero/index.ts new file mode 100644 index 00000000000..d49fb7397bf --- /dev/null +++ b/src/components/Hero/index.ts @@ -0,0 +1,4 @@ +export { default as ContentHero, type ContentHeroProps } from "./ContentHero" +export { default as HomeHero, type HomeHeroProps } from "./HomeHero" +export { default as HubHero, type HubHeroProps } from "./HubHero" +export { default as MdxHero, type MdxHeroProps } from "./MdxHero" diff --git a/src/components/Hero/utils.ts b/src/components/Hero/utils.ts new file mode 100644 index 00000000000..94e1afb2a2d --- /dev/null +++ b/src/components/Hero/utils.ts @@ -0,0 +1,10 @@ +import { IGatsbyImageData } from "gatsby-plugin-image" +import { CallToActionProps } from "./CallToAction" + +export type CommonHeroProps = { + heroImgSrc: IGatsbyImageData + header: string + title: string + description: string + buttons?: [CallToActionProps, CallToActionProps?] +} diff --git a/src/components/Morpher.tsx b/src/components/Morpher.tsx index 3e52a18d332..8e55b5ca07b 100644 --- a/src/components/Morpher.tsx +++ b/src/components/Morpher.tsx @@ -143,7 +143,7 @@ const Morpher = () => { diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 6bbcc690853..ee89c5452a8 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -10,7 +10,6 @@ import { Heading, HeadingProps, Icon, - Img, SimpleGridProps, Stack, useToken, @@ -25,14 +24,13 @@ import CalloutBanner from "../components/CalloutBanner" import CodeModal from "../components/CodeModal" import Codeblock from "../components/Codeblock" import CommunityEvents from "../components/CommunityEvents" -import Morpher from "../components/Morpher" import PageMetadata from "../components/PageMetadata" import StatsBoxGrid from "../components/StatsBoxGrid" import Translation from "../components/Translation" import TitleCardList, { ITitleCardItem } from "../components/TitleCardList" -import Text from "../components/OldText" import GatsbyImage from "../components/GatsbyImage" import WritersCohortBanner from "../components/Banners/Implementations/WritersCohortBanner" +import { HomeHero } from "../components/Hero" import { isLangRightToLeft } from "../utils/translations" import { getImage } from "../utils/image" @@ -153,30 +151,6 @@ const ButtonLinkRow = (props: ChildOnlyProp) => ( /> ) -const PageHeader = () => ( - - - - - - - - - - - - - -) - const HomePage = ({ data, pageContext: { language = "en" }, @@ -288,19 +262,9 @@ const HomePage = ({ title={t("page-index-meta-title")} description={t("page-index-meta-description")} /> - - - + + + {/* Getting Started Section */} diff --git a/src/pages/learn/index.tsx b/src/pages/learn/index.tsx index 0a2e63411bb..d38f2ea8102 100644 --- a/src/pages/learn/index.tsx +++ b/src/pages/learn/index.tsx @@ -22,7 +22,7 @@ import InlineLink from "../../components/Link" import OriginalCard, { IProps as IOriginalCardProps, } from "../../components/Card" -import PageHero from "../../components/PageHero" +import { HubHero, type HubHeroProps } from "../../components/Hero" import PageMetadata from "../../components/PageMetadata" import Translation from "../../components/Translation" import UpgradeTableOfContents from "../../components/UpgradeTableOfContents" @@ -166,12 +166,11 @@ const LearnPage = ({ data }: PageProps) => { }, ] - const heroContent = { + const heroContent: HubHeroProps = { title: t("learn-hub"), header: t("hero-header"), - subtitle: t("hero-subtitle"), - image: getImage(data.heroImage)!, - alt: "", + description: t("hero-subtitle"), + heroImgSrc: getImage(data.heroImage)!, buttons: [ { content: t("hero-button-lets-get-started"), @@ -189,11 +188,7 @@ const LearnPage = ({ data }: PageProps) => { - - - - - + ) => { w="full" mb={16} mx="auto" - pt={{ lg: 16 }} + pt={{ base: "10", lg: "16" }} dir={isRightToLeft ? "rtl" : "ltr"} > @@ -811,10 +806,10 @@ export const query = graphql` } } } - heroImage: file(relativePath: { eq: "eth.png" }) { + heroImage: file(relativePath: { eq: "heroes/learn-hub-hero.png" }) { childImageSharp { gatsbyImageData( - width: 500 + width: 1504 layout: CONSTRAINED placeholder: BLURRED quality: 100