From f676c5189b469afa1f8bb3ecec04cf638d1a459b Mon Sep 17 00:00:00 2001 From: Yvonne Tang Date: Thu, 5 Oct 2023 18:33:54 -0700 Subject: [PATCH] Story Hero MVP --- .../BlurryPoster/BlurryPoster.styles.tsx | 15 +- components/BlurryPoster/BlurryPoster.tsx | 18 ++- components/Hero/StoryHeroMvp.styles.ts | 100 +++++++++++++ components/Hero/StoryHeroMvp.tsx | 132 ++++++++++++++++++ components/Hero/index.ts | 1 + components/Masthead/Masthead.tsx | 2 +- components/Storyblok/SbStoryMvp.tsx | 129 +++++++++++++++++ components/StoryblokProvider.tsx | 2 + 8 files changed, 386 insertions(+), 13 deletions(-) create mode 100644 components/Hero/StoryHeroMvp.styles.ts create mode 100644 components/Hero/StoryHeroMvp.tsx create mode 100644 components/Storyblok/SbStoryMvp.tsx diff --git a/components/BlurryPoster/BlurryPoster.styles.tsx b/components/BlurryPoster/BlurryPoster.styles.tsx index e7a4d132..cfb15118 100644 --- a/components/BlurryPoster/BlurryPoster.styles.tsx +++ b/components/BlurryPoster/BlurryPoster.styles.tsx @@ -16,18 +16,19 @@ export const contentWrapper = (imageOnLeft: boolean) => cnb('relative z-10', { export const headingWrapper = (imageOnLeft: boolean) => cnb('lg:rs-mt-7 rs-mb-5', { 'lg:w-[120%] lg:-ml-[20%] 3xl:w-auto 3xl:-ml-200 lg:mr-0' : imageOnLeft, }); -export const headingInnerWrapper = (imageOnLeft: boolean) => cnb('w-full border-l-[1rem] sm:border-l-[1.4rem] md:border-l-[2rem] pl-10 pr-20 sm:pl-16 sm:pr-30 md:pl-30 md:pr-50', { +export const headingInnerWrapper = (imageOnLeft: boolean, headingFont?: 'druk' | 'serif') => cnb('w-full border-l-[1rem] sm:border-l-[1.4rem] md:border-l-[2rem] pl-10 pr-20 sm:pl-16 sm:pr-30 md:pl-30 md:pr-50', { 'lg:border-l-0 lg:border-r-[3rem] xl:border-r-[4rem] lg:pl-0 lg:pr-50 xl:pr-60': imageOnLeft, 'lg:border-l-[3rem] xl:border-l-[4rem] lg:pl-50 xl:pl-60 lg:pr-0 3xl:pl-[calc(100%-750px-40px)] lg:w-[140%] xl:w-[120%]': !imageOnLeft, }); -export const heading = (imageOnLeft: boolean, isSmallHeading: boolean) => cnb('mb-0 -mt-01em fluid-type-7', { - '3xl:pl-[calc(100%-750px-40px)] lg:w-[140%] xl:w-[130%]': !imageOnLeft, - 'md:fluid-type-8 lg:fluid-type-7 3xl:fluid-type-8 4xl:text-[17.1rem]': isSmallHeading, - 'md:fluid-type-9': !isSmallHeading, +export const heading = (imageOnLeft: boolean, isSmallHeading: boolean, headingFont?: 'druk' | 'serif') => cnb('mb-0 -mt-01em', { + '3xl:pl-[calc(100%-750px-40px)] lg:w-[140%] xl:w-[130%]': !imageOnLeft && headingFont === 'druk', + 'fluid-type-7 md:fluid-type-8 lg:fluid-type-7 3xl:fluid-type-8 4xl:text-[17.1rem]': isSmallHeading && headingFont === 'druk', + 'fluid-type-7 md:fluid-type-9': !isSmallHeading && headingFont === 'druk', + 'type-6' : headingFont === 'serif', }); -export const customHeading = (imageOnLeft: boolean) => cnb('flex flex-wrap gap-x-[1em] items-center mb-0 -mt-05em lg:-mt-08em children:inline-block', { - '3xl:pl-[calc(100%-750px-40px)] lg:w-[140%] xl:w-[130%]': !imageOnLeft, +export const customHeading = (imageOnLeft: boolean, headingFont?: 'druk' | 'serif') => cnb('flex flex-wrap gap-x-[1em] items-center mb-0 -mt-05em lg:-mt-08em children:inline-block', { + '3xl:pl-[calc(100%-750px-40px)] lg:w-[140%] xl:w-[130%]': !imageOnLeft && headingFont === 'druk', }); export const customHeadingText = (font: 'druk' | 'serif', isSmallHeading: boolean) => cnb('hyphens-auto first:ml-0 last:mr-0', { 'fluid-type-7': font === 'druk', diff --git a/components/BlurryPoster/BlurryPoster.tsx b/components/BlurryPoster/BlurryPoster.tsx index 0f402779..ebed0a44 100644 --- a/components/BlurryPoster/BlurryPoster.tsx +++ b/components/BlurryPoster/BlurryPoster.tsx @@ -18,6 +18,7 @@ type BlurryPosterProps = HTMLAttributes & { imageOnLeft?: boolean; headingLevel?: HeadingType; heading?: string; + headingFont?: 'serif' | 'druk'; customHeading?: SbTypographyProps[]; isSmallHeading?: boolean; addDarkOverlay?: boolean; @@ -35,6 +36,7 @@ export const BlurryPoster = ({ bgImageFocus, imageOnLeft, heading, + headingFont = 'druk', customHeading, headingLevel = 'h2', isSmallHeading, @@ -65,22 +67,28 @@ export const BlurryPoster = ({ {/* Render all Druk font heading if custom heading is not entered */} {heading && !customHeading?.length && ( {heading} )} {/* Render custom mixed typography heading if entered */} {!!customHeading?.length && ( - + {customHeading.map(({text, font, italic}) => ( cnb({ + 'rs-mt-10 xl:mt-[32.7rem]': !hasHeroImage, + 'lg:order-2': isLeftImage && isVerticalHero && hasHeroImage, + 'mt-40 lg:mb-0 xl:rs-mt-8': isVerticalHero && hasHeroImage, + 'rs-mb-6': hasHeroImage, + 'ml-0 rs-mt-9': !isVerticalHero && hasHeroImage, +}); + +export const tabSection = (hasTabColor: boolean, isVerticalHero: boolean, isLeftImage: boolean) => cnb( + 'rs-mb-0 cc', { + 'border-l-[1rem] sm:border-l-[1.4rem] md:border-l-[2rem] lg:border-l-[2.6rem]': hasTabColor, + '3xl:pl-0 3xl:pr-[calc(100%-75rem)]': isVerticalHero && isLeftImage, + '3xl:pr-0 3xl:pl-[calc(100%-75rem)]': isVerticalHero && !isLeftImage, + }, +); + +export const heading = ( + hasHeroImage: boolean, + hasTabColor: boolean, + isVerticalHero: boolean, + isLeftImage: boolean, +) => cnb( + 'mb-0', + isVerticalHero && hasHeroImage ? 'xl:max-w-700 type-5 lg:fluid-type-5' : 'xl:max-w-1200 type-5 lg:fluid-type-6', { + '-ml-10 sm:-ml-18 lg:-ml-26': hasTabColor, + '3xl:ml-[7.4rem]': isVerticalHero && hasHeroImage && isLeftImage && hasTabColor, + '3xl:ml-100': isVerticalHero && hasHeroImage && isLeftImage && !hasTabColor, + }, +); + +export const storyInfo = ( + hasHeroImage: boolean, + hasTabColor: boolean, + isVerticalHero: boolean, + isLeftImage: boolean, +) => cnb( + 'rs-mt-1 gc-card', + isVerticalHero && hasHeroImage ? 'xl:max-w-700' : 'xl:max-w-1200', { + '-ml-10 sm:-ml-18 lg:-ml-26': hasTabColor, + '3xl:ml-[7.4rem]': isVerticalHero && hasHeroImage && isLeftImage && hasTabColor, + '3xl:ml-100': isVerticalHero && hasHeroImage && isLeftImage && !hasTabColor, + }, +); + +export const chipDek = (hasHeroImage: boolean, isVerticalHero: boolean, isLeftImage: boolean) => cnb( + 'cc', { + '3xl:ml-100 3xl:pl-0 3xl:pr-[calc(100%-75rem)]': isVerticalHero && hasHeroImage && isLeftImage, + '3xl:pr-0 3xl:pl-[calc(100%-75rem)]': isVerticalHero && hasHeroImage && !isLeftImage, + }, +); + +export const body = (hasHeroImage: boolean, isVerticalHero: boolean) => cnb( + 'rs-mt-4 mb-0', + isVerticalHero && hasHeroImage ? 'xl:max-w-[56rem]' : 'max-w-prose', +); + +export const taxonomy = 'list-unstyled leading-display gap-x-19 gap-y-8'; +export const taxonomyItem = 'inline-block'; + +export const imageCrops = { + '1x1': '1200x1200', + '2x1': '2000x1000', + '3x2': '2100x1400', + '5x8': '1000x1600', + '16x9': '2000x1125', + 'free': '2000x0', +}; +export type ImageCropType = keyof typeof imageCrops; + +export const mobileImageCrops = { + '1x1': '1000x1000', + '2x1': '1000x500', + '3x2': '1200x800', + '5x8': '1000x1600', + '16x9': '1600x900', + 'free': '1000x0', +}; + +export const imageWrapper = (isVerticalHero: boolean, isLeftImage: boolean) => cnb('rounded-br-[16vw] overflow-hidden', { + 'lg:order-1': isLeftImage && isVerticalHero, +}); + +export const image = (renderTwoImages: boolean) => cnb( + 'w-full h-full', + renderTwoImages ? 'hidden lg:block' : '', +); +export const mobileImage = 'w-full h-full lg:hidden'; + +export const caption = (isVerticalHero: boolean, isLeftImage: boolean) => cnb('text-current rs-mt-0 cc type-0', { + 'lg:pr-0': isVerticalHero && isLeftImage, + 'lg:pl-0': isVerticalHero && !isLeftImage, +}); +export const captionText = (isVerticalHero: boolean, isLeftImage: boolean) => cnb(isVerticalHero ? 'max-w-prose-wide' : 'max-w-900', { + '3xl:pl-100 mr-auto': isVerticalHero && isLeftImage, + 'text-right 3xl:pr-100 ml-auto ': isVerticalHero && !isLeftImage, +}); diff --git a/components/Hero/StoryHeroMvp.tsx b/components/Hero/StoryHeroMvp.tsx new file mode 100644 index 00000000..29c0a302 --- /dev/null +++ b/components/Hero/StoryHeroMvp.tsx @@ -0,0 +1,132 @@ +import React from 'react'; +import { cnb } from 'cnbuilder'; +import { Container } from '../Container'; +import { BlurryPoster } from '../BlurryPoster'; +import { CtaLink } from '../Cta'; +import { Grid } from '../Grid'; +import { FlexBox } from '../FlexBox'; +import { Heading, Text, Paragraph } from '../Typography'; +import { paletteAccentColors, type PaletteAccentHexColorType } from '@/utilities/colorPalettePlugin'; +import { accentBorderColors, type AccentBorderColorType } from '@/utilities/datasource'; +import { getProcessedImage } from '@/utilities/getProcessedImage'; +import { slugify } from '@/utilities/slugify'; +import { type SbImageType } from '../Storyblok/Storyblok.types'; +import * as styles from './StoryHeroMvp.styles'; + +export type StoryHeroMvpProps = { + title: string; + headingFont?: 'serif' | 'druk' | 'druk-small'; + byline?: string; + publishedDate?: string; + dek?: string; + heroImage?: SbImageType; + bgImage?: SbImageType; + aspectRatio?: styles.ImageCropType; + mobileImage?: SbImageType; + mobileAspectRatio?: styles.ImageCropType; + imageOnLeft?: boolean; + alt?: string; + caption?: string; + isVerticalHero?: boolean; + isLeftImage?: boolean; + isLightHero?: boolean; + tabColor?: { + value?: PaletteAccentHexColorType; + } + topics?: string[]; +}; + +export const StoryHeroMvp = ({ + title, + headingFont, + byline, + dek, + publishedDate, + heroImage: { filename, focus } = {}, + mobileImage: { filename: mobileFilename, focus: mobileFocus } = {}, + bgImage: { filename: bgImageSrc, focus: bgImageFocus } = {}, + imageOnLeft, + alt, + caption, + isVerticalHero = false, + isLeftImage = false, + isLightHero = false, + aspectRatio = isVerticalHero ? '5x8' : '2x1', + mobileAspectRatio = '1x1', + tabColor: { value: tabColorValue } = {}, + topics, +}: StoryHeroMvpProps) => { + const useTwoColLayout = isVerticalHero && !!filename; + const date = publishedDate && new Date(publishedDate); + const formattedDate = date && date.toLocaleDateString('en-US', { + weekday: 'long', + month: 'long', + day: 'numeric', + year: 'numeric', + }); + + const cropSize = styles.imageCrops[aspectRatio]; + const cropWidth = parseInt(cropSize?.split('x')[0], 10); + const cropHeight = parseInt(cropSize?.split('x')[1], 10); + const mobileCropSize = styles.mobileImageCrops[mobileAspectRatio]; + const mobileCropWidth = parseInt(mobileCropSize?.split('x')[0], 10); + const mobileCropHeight = parseInt(mobileCropSize?.split('x')[1], 10); + + const renderOneImage = !!filename && !mobileFilename && mobileAspectRatio === aspectRatio; + // Render 2 different images if there is both a hero image or a mobile image + // Or when there is no mobile image, but the mobile aspect ratio is different from the desktop aspect ratio + const renderTwoImages = (!!filename && !!mobileFilename) || + (!!filename && !mobileFilename && mobileAspectRatio !== aspectRatio); + + // TODO: add srcset later in GIVCAMP-71 + // This one will always be rendered when there's a hero image + const RenderedDesktopImage = ( + {alt + ); + + // This one will only be rendered when the conditions in renderTwoImages are met + const RenderedMobileImage = renderTwoImages && ( + {alt + ); + + return ( + + + + ); +}; diff --git a/components/Hero/index.ts b/components/Hero/index.ts index dc797225..7a683c4d 100644 --- a/components/Hero/index.ts +++ b/components/Hero/index.ts @@ -1,2 +1,3 @@ export * from './Hero'; export * from './StoryHero'; +export * from './StoryHeroMvp'; diff --git a/components/Masthead/Masthead.tsx b/components/Masthead/Masthead.tsx index 567745be..b9267a0a 100644 --- a/components/Masthead/Masthead.tsx +++ b/components/Masthead/Masthead.tsx @@ -65,7 +65,7 @@ export const Masthead = ({ isLight, className }: MastheadProps) => { ( +
+ +
+
+ {!(title?.includes('Whereas') || title?.includes('Progress') || title?.includes('Video') || title?.includes('Solve')) && ( + <> + + + + )} + {title?.includes('Solve') && ( + + )} + {title?.includes('Progress') && ( + + )} + {title?.includes('Video scrolling') && ( + + )} + {title?.includes('Chatbot') && ( + + )} + {title?.includes('Whereas') && ( + + )} + + {title?.includes('bookshelf') && ( + + )} + {title?.includes('Immersive featured') && ( + <> + + + + + )} + {getNumBloks(blok.ankle) > 0 && ( + + )} + +
+
+
+); + diff --git a/components/StoryblokProvider.tsx b/components/StoryblokProvider.tsx index a88e192b..eed5f8d5 100644 --- a/components/StoryblokProvider.tsx +++ b/components/StoryblokProvider.tsx @@ -17,6 +17,7 @@ import { SbPortraitCard } from './Storyblok/SbPortraitCard'; import { SbSection } from './Storyblok/SbSection'; import { SbSplitPoster } from './Storyblok/SbSplitPoster'; import { SbStory } from './Storyblok/SbStory'; +import { SbStoryMvp } from './Storyblok/SbStoryMvp'; import { SbStoryCard } from './Storyblok/SbStoryCard'; import { SbTextCard } from './Storyblok/SbTextCard'; import { SbThemeCard } from './Storyblok/SbThemeCard'; @@ -40,6 +41,7 @@ const components = { sbSection: SbSection, sbSplitPoster: SbSplitPoster, sbStory: SbStory, + sbStoryMvp: SbStoryMvp, sbTextCard: SbTextCard, sbThemeCard: SbThemeCard, sbTriangle: SbTriangle,