Skip to content

Commit

Permalink
feat: Add the Marketing Banner component
Browse files Browse the repository at this point in the history
  • Loading branch information
LautaroPetaccio committed Jan 10, 2025
1 parent d2cfa98 commit 74fe898
Show file tree
Hide file tree
Showing 10 changed files with 560 additions and 0 deletions.
39 changes: 39 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
},
"homepage": "https://github.com/decentraland/ui2#readme",
"dependencies": {
"@contentful/rich-text-react-renderer": "^16.0.1",
"@dcl/schemas": "^13.9.0",
"@dcl/ui-env": "^1.5.1",
"@emotion/react": "^11.11.4",
Expand All @@ -55,6 +56,7 @@
"@babel/preset-env": "^7.24.7",
"@babel/preset-react": "^7.24.7",
"@babel/preset-typescript": "^7.24.7",
"@contentful/rich-text-types": "^17.0.0",
"@dcl/eslint-config": "^2.2.1",
"@storybook/addon-docs": "^7.6.20",
"@storybook/addon-essentials": "^7.6.20",
Expand Down
41 changes: 41 additions & 0 deletions src/components/MarketingBanner/MarketingBanner.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { MarketingBanner } from "./MarketingBanner"
import { Locales } from "../../hooks/contenful"
import type { Meta, StoryObj } from "@storybook/react"

const meta = {
title: "Decentraland UI/MarketingBanner",
component: MarketingBanner,
parameters: {
layout: "fullscreen",
},
tags: ["autodocs"],
} satisfies Meta<typeof MarketingBanner>

type Story = StoryObj<typeof MarketingBanner>

const Default: Story = {
args: {
id: "2maJ4UuoqaYtbZxVGymsJo",
environment: "development",
token: "AATjg5ZJgxllQW8PBSQV4ZaY5wh9W_lQSKIiERHY-sc",
space: "ea2ybdmmn1kv",
},
}

const SpanishLocale: Story = {
args: {
...Default.args,
locale: Locales.es,
},
}

const ChineseLocale: Story = {
args: {
...Default.args,
locale: Locales.zh,
},
}

export { Default, SpanishLocale, ChineseLocale }
// eslint-disable-next-line import/no-default-export
export default meta
142 changes: 142 additions & 0 deletions src/components/MarketingBanner/MarketingBanner.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import styled from "@emotion/styled"
import { Box, Button as MuiButton, Typography } from "@mui/material"
import type { Property } from "csstype"

const convertAlignmentToFlex = (alignment: Property.TextAlign) => {
switch (alignment) {
case "left":
return "flex-start"
case "center":
return "center"
case "right":
return "flex-end"
default:
return "flex-start"
}
}

const LoadingContainer = styled(Box)({
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "100%",
})

const BannerContainer = styled(Box, {
shouldForwardProp: (prop) =>
prop !== "mobileBackground" && prop !== "fullSizeBackground",
})<{
mobileBackground: string
fullSizeBackground: string
}>(({ mobileBackground, fullSizeBackground }) => ({
width: "100%",
overflow: "hidden",
display: "flex",
padding: "2rem",
flexDirection: "column-reverse",
justifyContent: "space-between",
backgroundImage: `url(${mobileBackground})`,
backgroundSize: "cover",
backgroundPosition: "center",
alignItems: "center",
"@media (min-width: 768px)": {
flexDirection: "row",
backgroundImage: `url(${fullSizeBackground})`,
},
}))

const ContentWrapper = styled(Box)({
display: "flex",
flexDirection: "row",
flexGrow: 1,
marginRight: "20px",
})

const Content = styled(Box)({
display: "flex",
flexDirection: "column",
gap: "0.1rem",
"@media (max-width: 767px)": {
padding: "1rem",
},
})

const Logo = styled("img")({
flexShrink: 0,
maxWidth: "400px",
"@media (max-width: 767px)": {
maxWidth: "300px",
marginBottom: "1rem",
},
})

const Title = styled(Typography, {
shouldForwardProp: (prop) =>
prop !== "mobileTitleAlignment" && prop !== "desktopTitleAlignment",
})<{
mobileTitleAlignment?: Property.TextAlign
desktopTitleAlignment?: Property.TextAlign
}>(({ mobileTitleAlignment, desktopTitleAlignment }) => ({
margin: 0,
color: "#fff",
textAlign: desktopTitleAlignment || "left",
fontSize: "28px",
textTransform: "uppercase",
fontWeight: 800,
"@media (max-width: 767px)": {
textAlign: mobileTitleAlignment || "left",
fontSize: "24px",
},
}))

const Text = styled(Box, {
shouldForwardProp: (prop) =>
prop !== "mobileTextAlignment" && prop !== "desktopTextAlignment",
})<{
mobileTextAlignment?: Property.TextAlign
desktopTextAlignment?: Property.TextAlign
}>(({ mobileTextAlignment, desktopTextAlignment }) => ({
color: "#fff",
textAlign: desktopTextAlignment || "left",
fontSize: "19px",
"& p": {
margin: 0,
padding: 0,
},
"@media (max-width: 767px)": {
textAlign: mobileTextAlignment || "left",
fontSize: "16px",
},
}))

const ButtonContainer = styled(Box, {
shouldForwardProp: (prop) =>
prop !== "mobileAlignment" && prop !== "desktopAlignment",
})<{
mobileAlignment?: Property.TextAlign
desktopAlignment?: Property.TextAlign
}>(({ mobileAlignment, desktopAlignment }) => ({
display: "flex",
marginTop: "1rem",
alignItems: convertAlignmentToFlex(desktopAlignment || "left"),
"@media (max-width: 767px)": {
alignItems: convertAlignmentToFlex(mobileAlignment || "left"),
},
}))

const Button = styled(MuiButton)({
textTransform: "uppercase",
minWidth: "300px",
})

export {
LoadingContainer,
BannerContainer,
Content,
ContentWrapper,
Logo,
Title,
Text,
ButtonContainer,
Button,
}
128 changes: 128 additions & 0 deletions src/components/MarketingBanner/MarketingBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import React from "react"
import { documentToReactComponents } from "@contentful/rich-text-react-renderer"
import CircularProgress from "@mui/material/CircularProgress"
import {
Locales,
getAssetUrl,
useGetContentfulEntry,
} from "../../hooks/contenful"
import {
IBannerFields,
LowercasedAlignment,
MarketingBannerProps,
} from "./MarketingBanner.types"
import {
BannerContainer,
Button,
ButtonContainer,
Content,
LoadingContainer,
Logo,
Text,
Title,
} from "./MarketingBanner.styled"

export const MarketingBanner: React.FC<MarketingBannerProps> = ({
id,
environment,
token,
space,
locale = Locales.enUS,
}) => {
const { fields, assets, isLoading, error } =
useGetContentfulEntry<IBannerFields>(id, environment, token, space)

if (isLoading) {
return (
<LoadingContainer>
<CircularProgress />
</LoadingContainer>
)
}

// If there is no banner fields or the banner is not supposed to be shown, return null
if (!fields || !fields.showBanner[locale] || error) {
return null
}

return (
<BannerContainer
mobileBackground={getAssetUrl(
assets,
locale,
fields.mobileBackground[locale]
)}
fullSizeBackground={getAssetUrl(
assets,
locale,
fields.fullSizeBackground[locale]
)}
>
<Content>
<Title
variant="h1"
mobileTitleAlignment={
fields.mobileTitleAlignment[
locale
]?.toLowerCase() as LowercasedAlignment
}
desktopTitleAlignment={
fields.desktopTitleAlignment[
locale
]?.toLowerCase() as LowercasedAlignment
}
>
{fields.title[locale]}
</Title>

<Text
desktopTextAlignment={
fields.desktopTextAlignment[
locale
]?.toLowerCase() as LowercasedAlignment
}
mobileTextAlignment={
fields.mobileTextAlignment[
locale
]?.toLowerCase() as LowercasedAlignment
}
>
{fields.text[locale]
? documentToReactComponents(fields.text[locale])
: null}
</Text>

{fields.showButton[locale] &&
fields.buttonLink?.[locale] &&
fields.buttonsText?.[locale] ? (
<ButtonContainer
desktopAlignment={
fields.desktopButtonAlignment[
locale
]?.toLowerCase() as LowercasedAlignment
}
mobileAlignment={
fields.mobileButtonAlignment[
locale
]?.toLowerCase() as LowercasedAlignment
}
>
<Button
href={fields.buttonLink[locale]}
variant="contained"
disableElevation
>
{fields.buttonsText[locale]}
</Button>
</ButtonContainer>
) : null}
</Content>
{fields.logo && fields.logo[locale] && (
<Logo
src={getAssetUrl(assets, locale, fields.logo[locale])}
alt="Banner logo"
/>
)}
</BannerContainer>
)
}
Loading

0 comments on commit 74fe898

Please sign in to comment.