From 6dd88baa01f4053a4e2638997f89da2e85799347 Mon Sep 17 00:00:00 2001 From: Lautaro Petaccio <1120791+LautaroPetaccio@users.noreply.github.com> Date: Wed, 15 Jan 2025 15:24:44 -0300 Subject: [PATCH] feat: Add Banner component and export hooks (#221) * feat: Add Banner component and export hooks * fix: Styling * fix: Use color from theme --- src/components/Banner/Banner.styled.ts | 121 +++++++++++++ src/components/Banner/Banner.tsx | 112 ++++++++++++ src/components/Banner/Banner.types.ts | 41 +++++ src/components/Banner/index.ts | 2 + .../MarketingBanner.stories.tsx | 2 +- .../MarketingBanner/MarketingBanner.styled.ts | 166 ------------------ .../MarketingBanner/MarketingBanner.tsx | 123 ++----------- .../MarketingBanner/MarketingBanner.types.ts | 31 +--- .../{contenful => contentful}/contentful.ts | 0 .../contentful.types.ts | 4 +- src/hooks/{contenful => contentful}/index.ts | 0 src/hooks/index.ts | 1 + src/index.ts | 2 + 13 files changed, 295 insertions(+), 310 deletions(-) create mode 100644 src/components/Banner/Banner.styled.ts create mode 100644 src/components/Banner/Banner.tsx create mode 100644 src/components/Banner/Banner.types.ts create mode 100644 src/components/Banner/index.ts delete mode 100644 src/components/MarketingBanner/MarketingBanner.styled.ts rename src/hooks/{contenful => contentful}/contentful.ts (100%) rename src/hooks/{contenful => contentful}/contentful.types.ts (93%) rename src/hooks/{contenful => contentful}/index.ts (100%) create mode 100644 src/hooks/index.ts diff --git a/src/components/Banner/Banner.styled.ts b/src/components/Banner/Banner.styled.ts new file mode 100644 index 0000000..1c2ea97 --- /dev/null +++ b/src/components/Banner/Banner.styled.ts @@ -0,0 +1,121 @@ +import styled from "@emotion/styled" +import { Box, Button as MuiButton, Typography } from "@mui/material" +import { neutral } from "../../theme/colors" + +const LoadingContainer = styled(Box)({ + display: "flex", + justifyContent: "center", + alignItems: "center", + width: "100%", +}) + +const BannerContainer = styled(Box, { + shouldForwardProp: (prop) => prop !== "background", +})<{ + background: string +}>((props) => { + const { theme, background } = props + + return { + width: "100%", + overflow: "hidden", + display: "flex", + padding: "2rem", + justifyContent: "space-between", + flexDirection: "row", + backgroundImage: `url(${background})`, + backgroundSize: "cover", + backgroundPosition: "center", + alignItems: "center", + [theme.breakpoints.down("sm")]: { + flexDirection: "column-reverse", + }, + } +}) + +const ContentWrapper = styled(Box)({ + display: "flex", + flexDirection: "row", + flexGrow: 1, + marginRight: "20px", +}) + +const Content = styled(Box)((props) => { + const { theme } = props + + return { + display: "flex", + flexDirection: "column", + gap: "0.1rem", + [theme.breakpoints.down("sm")]: { + padding: "1rem", + }, + } +}) + +const Logo = styled("img")((props) => { + const { theme } = props + + return { + flexShrink: 0, + maxWidth: "400px", + [theme.breakpoints.down("sm")]: { + maxWidth: "300px", + marginBottom: "1rem", + }, + } +}) + +const Title = styled(Typography)((props) => { + const { theme } = props + + return { + margin: 0, + color: "#fff", + fontSize: "28px", + textTransform: "uppercase", + fontWeight: 800, + + [theme.breakpoints.down("sm")]: { + fontSize: "24px", + }, + } +}) + +const Text = styled(Box)((props) => { + const { theme } = props + + return { + color: neutral.white, + fontSize: "19px", + "& p": { + margin: 0, + padding: 0, + }, + [theme.breakpoints.down("sm")]: { + fontSize: "16px", + }, + } +}) + +const ButtonContainer = styled(Box)({ + display: "flex", + marginTop: "1rem", +}) + +const Button = styled(MuiButton)({ + textTransform: "uppercase", + minWidth: "300px", +}) + +export { + LoadingContainer, + BannerContainer, + Content, + ContentWrapper, + Logo, + Title, + Text, + ButtonContainer, + Button, +} diff --git a/src/components/Banner/Banner.tsx b/src/components/Banner/Banner.tsx new file mode 100644 index 0000000..e3aa1fc --- /dev/null +++ b/src/components/Banner/Banner.tsx @@ -0,0 +1,112 @@ +import { documentToReactComponents } from "@contentful/rich-text-react-renderer" +import CircularProgress from "@mui/material/CircularProgress" +import { Locales, getAssetUrl } from "../../hooks/contentful" +import { useTabletAndBelowMediaQuery } from "../Media" +import { BannerProps, LowercasedAlignment } from "./Banner.types" +import { + BannerContainer, + Button, + ButtonContainer, + Content, + LoadingContainer, + Logo, + Text, + Title, +} from "./Banner.styled" +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" + } +} + +export const Banner: React.FC = (props: BannerProps) => { + const { isLoading, fields, assets, locale = Locales.enUS, error } = props + const isMobileOrTablet = useTabletAndBelowMediaQuery() + + if (isLoading) { + return ( + + + + ) + } + + // If there is no banner fields or the banner is not supposed to be shown, return null + if (!fields || !fields.showBanner[Locales.enUS] || error) { + return null + } + + // Build the parameteres based on the size of the screen + const bannerBackgroundImage = getAssetUrl( + assets, + Locales.enUS, + isMobileOrTablet + ? fields.mobileBackground[Locales.enUS] + : fields.fullSizeBackground[Locales.enUS] + ) + const title = isMobileOrTablet + ? fields.mobileTitle[locale] + : fields.desktopTitle[locale] + const titleAlignment = ( + isMobileOrTablet + ? fields.mobileTitleAlignment[Locales.enUS] + : fields.desktopTitleAlignment[Locales.enUS] + )?.toLowerCase() as LowercasedAlignment + const text = isMobileOrTablet + ? fields.mobileText[locale] + : fields.desktopText[locale] + const textAlignment = ( + isMobileOrTablet + ? fields.mobileTextAlignment[Locales.enUS] + : fields.desktopTextAlignment[Locales.enUS] + )?.toLowerCase() as LowercasedAlignment + const buttonAlignment = convertAlignmentToFlex( + (isMobileOrTablet + ? fields.mobileButtonAlignment[Locales.enUS] + : fields.desktopButtonAlignment[Locales.enUS] + )?.toLowerCase() as LowercasedAlignment + ) + + return ( + + + + {title} + + + + {text ? documentToReactComponents(text) : null} + + + {fields.showButton[Locales.enUS] && + fields.buttonLink?.[Locales.enUS] && + fields.buttonsText?.[locale] ? ( + + + + ) : null} + + {fields.logo && fields.logo[Locales.enUS] && ( + + )} + + ) +} diff --git a/src/components/Banner/Banner.types.ts b/src/components/Banner/Banner.types.ts new file mode 100644 index 0000000..739ca77 --- /dev/null +++ b/src/components/Banner/Banner.types.ts @@ -0,0 +1,41 @@ +import { + AlignmentFieldType, + ContentfulAsset, + Locales, + LocalizedField, + SysAssetLink, +} from "hooks/contentful" +import type { Document } from "@contentful/rich-text-types" + +type IBannerFields = { + id: LocalizedField + showBanner: LocalizedField + desktopTitle: LocalizedField + mobileTitle: LocalizedField + mobileTitleAlignment: LocalizedField + desktopTitleAlignment: LocalizedField + desktopText: LocalizedField + mobileText: LocalizedField + desktopTextAlignment: LocalizedField + mobileTextAlignment: LocalizedField + showButton: LocalizedField + buttonLink?: LocalizedField + buttonsText?: LocalizedField + desktopButtonAlignment: LocalizedField + mobileButtonAlignment: LocalizedField + fullSizeBackground: LocalizedField + mobileBackground: LocalizedField + logo?: LocalizedField +} + +type BannerProps = { + fields: IBannerFields | null + assets: Record + isLoading: boolean + locale?: Locales + error: string | null +} + +type LowercasedAlignment = "left" | "center" | "right" + +export type { BannerProps, IBannerFields, LowercasedAlignment } diff --git a/src/components/Banner/index.ts b/src/components/Banner/index.ts new file mode 100644 index 0000000..0eef469 --- /dev/null +++ b/src/components/Banner/index.ts @@ -0,0 +1,2 @@ +export { Banner } from "./Banner" +export type { BannerProps, IBannerFields } from "./Banner.types" diff --git a/src/components/MarketingBanner/MarketingBanner.stories.tsx b/src/components/MarketingBanner/MarketingBanner.stories.tsx index c0e95ce..6784d39 100644 --- a/src/components/MarketingBanner/MarketingBanner.stories.tsx +++ b/src/components/MarketingBanner/MarketingBanner.stories.tsx @@ -1,5 +1,5 @@ import { MarketingBanner } from "./MarketingBanner" -import { Locales } from "../../hooks/contenful" +import { Locales } from "../../hooks/contentful" import type { Meta, StoryObj } from "@storybook/react" const meta = { diff --git a/src/components/MarketingBanner/MarketingBanner.styled.ts b/src/components/MarketingBanner/MarketingBanner.styled.ts deleted file mode 100644 index ce3eafe..0000000 --- a/src/components/MarketingBanner/MarketingBanner.styled.ts +++ /dev/null @@ -1,166 +0,0 @@ -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 -}>((props) => { - const { theme, mobileBackground, fullSizeBackground } = props - - return { - width: "100%", - overflow: "hidden", - display: "flex", - padding: "2rem", - justifyContent: "space-between", - flexDirection: "row", - backgroundImage: `url(${fullSizeBackground})`, - backgroundSize: "cover", - backgroundPosition: "center", - alignItems: "center", - [theme.breakpoints.down("sm")]: { - backgroundImage: `url(${mobileBackground})`, - flexDirection: "column-reverse", - }, - } -}) - -const ContentWrapper = styled(Box)({ - display: "flex", - flexDirection: "row", - flexGrow: 1, - marginRight: "20px", -}) - -const Content = styled(Box)((props) => { - const { theme } = props - - return { - display: "flex", - flexDirection: "column", - gap: "0.1rem", - [theme.breakpoints.down("sm")]: { - padding: "1rem", - }, - } -}) - -const Logo = styled("img")((props) => { - const { theme } = props - - return { - flexShrink: 0, - maxWidth: "400px", - [theme.breakpoints.down("sm")]: { - maxWidth: "300px", - marginBottom: "1rem", - }, - } -}) - -const Title = styled(Typography, { - shouldForwardProp: (prop) => - prop !== "mobileTitleAlignment" && prop !== "desktopTitleAlignment", -})<{ - mobileTitleAlignment?: Property.TextAlign - desktopTitleAlignment?: Property.TextAlign -}>((props) => { - const { theme, mobileTitleAlignment, desktopTitleAlignment } = props - - return { - margin: 0, - color: "#fff", - textAlign: desktopTitleAlignment || "left", - fontSize: "28px", - textTransform: "uppercase", - fontWeight: 800, - [theme.breakpoints.down("sm")]: { - textAlign: mobileTitleAlignment || "left", - fontSize: "24px", - }, - } -}) - -const Text = styled(Box, { - shouldForwardProp: (prop) => - prop !== "mobileTextAlignment" && prop !== "desktopTextAlignment", -})<{ - mobileTextAlignment?: Property.TextAlign - desktopTextAlignment?: Property.TextAlign -}>((props) => { - const { theme, mobileTextAlignment, desktopTextAlignment } = props - - return { - color: "#fff", - textAlign: desktopTextAlignment || "left", - fontSize: "19px", - "& p": { - margin: 0, - padding: 0, - }, - [theme.breakpoints.down("sm")]: { - textAlign: mobileTextAlignment || "left", - fontSize: "16px", - }, - } -}) - -const ButtonContainer = styled(Box, { - shouldForwardProp: (prop) => - prop !== "mobileAlignment" && prop !== "desktopAlignment", -})<{ - mobileAlignment?: Property.TextAlign - desktopAlignment?: Property.TextAlign -}>((props) => { - const { theme, mobileAlignment, desktopAlignment } = props - - return { - display: "flex", - marginTop: "1rem", - alignItems: convertAlignmentToFlex(desktopAlignment || "left"), - [theme.breakpoints.down("sm")]: { - alignItems: convertAlignmentToFlex(mobileAlignment || "left"), - }, - } -}) - -const Button = styled(MuiButton)({ - textTransform: "uppercase", - minWidth: "300px", -}) - -export { - LoadingContainer, - BannerContainer, - Content, - ContentWrapper, - Logo, - Title, - Text, - ButtonContainer, - Button, -} diff --git a/src/components/MarketingBanner/MarketingBanner.tsx b/src/components/MarketingBanner/MarketingBanner.tsx index d5e486a..01e6515 100644 --- a/src/components/MarketingBanner/MarketingBanner.tsx +++ b/src/components/MarketingBanner/MarketingBanner.tsx @@ -1,26 +1,7 @@ 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" +import { Banner, IBannerFields } from "../../components/Banner" +import { Locales, useGetContentfulEntry } from "../../hooks/contentful" +import { MarketingBannerProps } from "./MarketingBanner.types" const BANNER_CONTENT_TYPE = "banner" @@ -37,97 +18,13 @@ export const MarketingBanner: React.FC = ( space ) - if (isLoading) { - return ( - - - - ) - } - - // 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 ( - - - - {fields.title[locale]} - - - - {fields.text[locale] - ? documentToReactComponents(fields.text[locale]) - : null} - - - {fields.showButton[locale] && - fields.buttonLink?.[locale] && - fields.buttonsText?.[locale] ? ( - - - - ) : null} - - {fields.logo && fields.logo[locale] && ( - - )} - + ) } diff --git a/src/components/MarketingBanner/MarketingBanner.types.ts b/src/components/MarketingBanner/MarketingBanner.types.ts index a928a3a..db91420 100644 --- a/src/components/MarketingBanner/MarketingBanner.types.ts +++ b/src/components/MarketingBanner/MarketingBanner.types.ts @@ -1,31 +1,4 @@ -import { - AlignmentFieldType, - Locales, - LocalizedField, - SysAssetLink, -} from "hooks/contenful" -import type { Document } from "@contentful/rich-text-types" - -type LowercasedAlignment = "left" | "center" | "right" - -type IBannerFields = { - id: LocalizedField - showBanner: LocalizedField - title: LocalizedField - mobileTitleAlignment: LocalizedField - desktopTitleAlignment: LocalizedField - text: LocalizedField - desktopTextAlignment: LocalizedField - mobileTextAlignment: LocalizedField - showButton: LocalizedField - buttonLink?: LocalizedField - buttonsText?: LocalizedField - desktopButtonAlignment: LocalizedField - mobileButtonAlignment: LocalizedField - fullSizeBackground: LocalizedField - mobileBackground: LocalizedField - logo?: LocalizedField -} +import { Locales } from "hooks/contentful" type MarketingBannerProps = { id: string @@ -35,4 +8,4 @@ type MarketingBannerProps = { locale?: Locales } -export type { LowercasedAlignment, IBannerFields, MarketingBannerProps } +export type { MarketingBannerProps } diff --git a/src/hooks/contenful/contentful.ts b/src/hooks/contentful/contentful.ts similarity index 100% rename from src/hooks/contenful/contentful.ts rename to src/hooks/contentful/contentful.ts diff --git a/src/hooks/contenful/contentful.types.ts b/src/hooks/contentful/contentful.types.ts similarity index 93% rename from src/hooks/contenful/contentful.types.ts rename to src/hooks/contentful/contentful.types.ts index 457629e..7fb17c8 100644 --- a/src/hooks/contenful/contentful.types.ts +++ b/src/hooks/contentful/contentful.types.ts @@ -7,7 +7,9 @@ enum ContentfulLocale { } type LocalizedField = { - [key in ContentfulLocale]: T + [ContentfulLocale.enUS]: T + [ContentfulLocale.es]?: T + [ContentfulLocale.zh]?: T } type SysAssetLink = { diff --git a/src/hooks/contenful/index.ts b/src/hooks/contentful/index.ts similarity index 100% rename from src/hooks/contenful/index.ts rename to src/hooks/contentful/index.ts diff --git a/src/hooks/index.ts b/src/hooks/index.ts new file mode 100644 index 0000000..5451740 --- /dev/null +++ b/src/hooks/index.ts @@ -0,0 +1 @@ +export * from "./contentful" diff --git a/src/index.ts b/src/index.ts index b23bef1..286e654 100644 --- a/src/index.ts +++ b/src/index.ts @@ -29,5 +29,7 @@ export * from "./components/Navbar" export * from "./components/Notifications" export * from "./components/UserMenu" export * from "./components/MarketingBanner" +export * from "./components/Banner" +export * from "./hooks" export * as dclNetworkUtils from "./lib/network"