+
+
{title && }
{ingress && (
@@ -166,7 +185,7 @@ const VideoPlayer = ({ anchor, data, className }: { data: VideoPlayerData; ancho
)}
-
+
)
diff --git a/web/pageComponents/shared/portableText/Blocks.tsx b/web/pageComponents/shared/portableText/Blocks.tsx
index 31aec9d7c..7581e6d40 100644
--- a/web/pageComponents/shared/portableText/Blocks.tsx
+++ b/web/pageComponents/shared/portableText/Blocks.tsx
@@ -21,8 +21,21 @@ type TypeProps = {
children?: React.ReactNode
}
-const defaultBlocks: BlockType = {
+/* const defaultBlocks: BlockType = {
smallText: ({ children }: TypeProps) =>
{children}
,
+ largeText: ({ children,className }: TypeProps) =>
{children}
,
+ extraLargeText: ({ children, className }: TypeProps) => {
+ return
{children}
+ },
+} */
+const defaultBlocks = (className?: string): BlockType => {
+ return {
+ smallText: ({ children }: TypeProps) =>
{children},
+ largeText: ({ children }: TypeProps) =>
{children},
+ extraLargeText: ({ children }: TypeProps) => {
+ return
{children}
+ },
+ }
}
const defaultMarks: MarkType = {
@@ -68,6 +81,7 @@ type BlockProps = {
* Override other styling to the wrapping block
*/
className?: string
+ blocksClassName?: string
/**
* If needed to connect with aria-describedby and such
*/
@@ -84,6 +98,7 @@ export default function Blocks({
types,
components,
proseClassName = '',
+ blocksClassName = '',
className = '',
id,
}: BlockProps) {
@@ -93,7 +108,7 @@ export default function Blocks({
...defaultComponents,
block: {
...defaultComponents.block,
- ...defaultBlocks,
+ ...defaultBlocks(blocksClassName),
...blocks,
} as BlockType,
marks: {
diff --git a/web/pageComponents/shared/textTeaser/theme.ts b/web/pageComponents/shared/textTeaser/theme.ts
index 097750938..c3dccdcc3 100644
--- a/web/pageComponents/shared/textTeaser/theme.ts
+++ b/web/pageComponents/shared/textTeaser/theme.ts
@@ -1,13 +1,12 @@
+import { ColorKeyTokens } from '../../../styles/colorKeyToUtilityMap'
import { BackgroundColours } from '../../../types'
export type ThemeColors = {
background: BackgroundColours
+ backgroundUtility?: keyof ColorKeyTokens
highlight?: string
+ textUtility?: keyof ColorKeyTokens
dark?: boolean
- utility?: {
- background?: string
- highlight?: string
- }
}
//Keep in sync with sanityv3/schemas/components/ThemeSelector/themeColors
/*export const getColorForTheme = (pattern: number): ThemeColors => {
@@ -108,10 +107,27 @@ export const getColorForTheme = (pattern: number): ThemeColors => {
case 7:
return { background: 'Mid Blue', highlight: 'var(--bg-mid-yellow)' }
case 8:
+ return {
+ background: 'Mid Blue',
+ backgroundUtility: 'blue-50',
+ highlight: 'var(--bg-white)',
+ textUtility: 'white-100',
+ }
+ case 9:
return { background: 'Mid Green', highlight: 'black' }
+ case 10:
+ return {
+ background: 'Mist Blue',
+ backgroundUtility: 'mist-blue-100',
+ textUtility: 'blue-50',
+ }
case 0:
default:
- return { background: 'White' }
+ return {
+ background: 'White',
+ backgroundUtility: 'white-100',
+ textUtility: 'energy-red-100',
+ }
}
}
diff --git a/web/sections/CampaignBanner/CampaignBanner.tsx b/web/sections/CampaignBanner/CampaignBanner.tsx
new file mode 100644
index 000000000..76c48c84a
--- /dev/null
+++ b/web/sections/CampaignBanner/CampaignBanner.tsx
@@ -0,0 +1,111 @@
+import { forwardRef, HTMLAttributes } from 'react'
+import { useSanityLoader } from '../../lib/hooks/useSanityLoader'
+import { twMerge } from 'tailwind-merge'
+import { CampaignBannerData } from '../../types/types'
+import Blocks from '../../pageComponents/shared/portableText/Blocks'
+import { PortableTextBlock } from '@portabletext/types'
+import isEmpty from '../../pageComponents/shared/portableText/helpers/isEmpty'
+import { BlockType } from '../../pageComponents/shared/portableText/helpers/defaultSerializers'
+
+const DEFAULT_MAX_WIDTH = 1920
+
+/* eslint-disable @typescript-eslint/ban-ts-comment */
+const campaignTitleBlocks: BlockType = {
+ //@ts-ignore
+ smallText: ({ children }: PortableTextBlock) =>
{<>{children}>},
+ //@ts-ignore
+ largeText: ({ children }: PortableTextBlock) => (
+
{<>{children}>}
+ ),
+ //@ts-ignore
+ extraLargeText: ({ children }: PortableTextBlock) => {
+ return (
+
+ {<>{children}>}
+
+ )
+ },
+ //@ts-ignore
+ normal: ({ children }: PortableTextBlock) => {
+ if (isEmpty(children)) return null
+ return (
+
+ <>{children}>
+
+ )
+ },
+}
+/* eslint-enable @typescript-eslint/ban-ts-comment */
+
+export type CampaignBannerProps = {
+ data: CampaignBannerData
+ className?: string
+} & HTMLAttributes
+
+const CampaignBanner = forwardRef(function CampaignBanner({ data, className }, ref) {
+ const {
+ title,
+ text,
+ backgroundImage,
+ backgroundColor,
+ backgroundUtility,
+ useLightOverlay = false,
+ attribution,
+ } = data
+ console.log('CampaignBanner', data)
+ const props = useSanityLoader(backgroundImage, DEFAULT_MAX_WIDTH, undefined)
+ const src = props?.src
+
+ const backgroundClassNames = twMerge(
+ `[container:inline-size]
+ relative
+ ${useLightOverlay ? '' : 'dark'}
+ w-full
+ 2xl:aspect-[10/3]
+ bg-local
+ bg-center
+ bg-no-repeat
+ bg-cover
+ mb-40
+ `,
+ className,
+ )
+
+ const scrimGradient = useLightOverlay ? `white-center-gradient ` : `black-center-gradient`
+
+ return (
+
+ )
+})
+export default CampaignBanner
diff --git a/web/sections/CampaignBanner/index.ts b/web/sections/CampaignBanner/index.ts
new file mode 100644
index 000000000..b132edd2a
--- /dev/null
+++ b/web/sections/CampaignBanner/index.ts
@@ -0,0 +1,2 @@
+//"use client";
+export { default as CampaignBanner, type CampaignBannerProps } from './CampaignBanner'
diff --git a/web/sections/Grid/Grid.tsx b/web/sections/Grid/Grid.tsx
new file mode 100644
index 000000000..47f8e6ed2
--- /dev/null
+++ b/web/sections/Grid/Grid.tsx
@@ -0,0 +1,49 @@
+import { twMerge } from 'tailwind-merge'
+import { GridData } from '../../types/types'
+import { HTMLAttributes, forwardRef } from 'react'
+import Span3 from './Span3'
+import Span2And1 from './Span2And1'
+import ThreeColumns from './ThreeColumns'
+
+export type GridProps = {
+ data: GridData
+ anchor?: string
+ className?: string
+} & HTMLAttributes
+
+const Grid = forwardRef(function Grid({ data, anchor, className = '', ...rest }, ref) {
+ const { gridRows = [] } = data
+ return (
+
+ {gridRows.map((row) => {
+ return (
+ <>
+ {row?.type === 'span3' && }
+ {row?.type === 'span2and1' && }
+ {row?.type === 'threeColumns' && }
+ >
+ )
+ })}
+
+ )
+})
+
+export default Grid
diff --git a/web/sections/Grid/GridFigure.tsx b/web/sections/Grid/GridFigure.tsx
new file mode 100644
index 000000000..42a33120e
--- /dev/null
+++ b/web/sections/Grid/GridFigure.tsx
@@ -0,0 +1,21 @@
+/* eslint-disable @typescript-eslint/no-unused-vars */
+import Image from '../../pageComponents/shared/SanityImage'
+import type { FigureData } from '../../types/types'
+
+type GridFigureProps = {
+ data: FigureData
+ className?: string
+}
+
+const GridFigure = ({ data }: GridFigureProps) => {
+ console.log('data', data)
+ const { figure, designOptions } = data
+ const { image, caption, attribution } = figure
+ return (
+
+
+
+ )
+}
+
+export default GridFigure
diff --git a/web/sections/Grid/GridLinkArrow.tsx b/web/sections/Grid/GridLinkArrow.tsx
new file mode 100644
index 000000000..8bbbfac3a
--- /dev/null
+++ b/web/sections/Grid/GridLinkArrow.tsx
@@ -0,0 +1,70 @@
+/* eslint-disable @typescript-eslint/ban-ts-comment */
+import { twMerge } from 'tailwind-merge'
+import { getUrlFromAction } from '../../common/helpers'
+import { BaseLink } from '@core/Link'
+import { getLocaleFromName } from '../../lib/localization'
+import { ArrowRight } from '../../icons'
+import { LinkData } from '../../types/types'
+import { getColorForTheme } from '../../pageComponents/shared/textTeaser/theme'
+import { colorKeyToUtilityMap } from '../../styles/colorKeyToUtilityMap'
+
+//type Theme = 'redOnWhite' | 'whiteOnDarkBlue' | 'darkBlueOnLightBlue'
+
+type GridLinkArrowProps = {
+ theme: string
+ action?: LinkData
+ className?: string
+}
+
+const GridLinkArrow = ({ theme, action, className }: GridLinkArrowProps) => {
+ const url = action && getUrlFromAction(action)
+ //@ts-ignore
+ const { backgroundUtility, textUtility } = getColorForTheme(theme)
+ let bgClassName = ''
+ let textClassName = ''
+ if (theme !== null) {
+ //@ts-ignore
+ bgClassName = colorKeyToUtilityMap[backgroundUtility]?.background
+ //@ts-ignore
+ textClassName = colorKeyToUtilityMap[textUtility]?.text
+ }
+
+ const variantClassName = () => {
+ switch (backgroundUtility) {
+ case 'white-100':
+ return 'text-energy-red-100 hover:bg-energy-red-100 hover:text-white-100 focus-visible:bg-energy-red-100 focus-visible:text-white-100'
+ case 'mist-blue-100':
+ return `${textClassName} hover:bg-white-100 hover:${textClassName} focus-visible:bg-white-100 focus-visible:${textClassName}`
+ case 'blue-50':
+ return `text-white-100 hover:bg-white-100 hover:text-blue-50 focus-visible:bg-white-100 focus-visible:text-blue-50`
+ default:
+ return 'text-white-100 hover:bg-white-100 hover:text-slate-80 focus-visible:bg-white-100 focus-visible:text-slate-80'
+ }
+ }
+
+ return (
+ <>
+ {action && url && (
+
+
+ {`${action.label} ${
+ action.extension ? `(${action.extension.toUpperCase()})` : ''
+ }`}
+
+
+
+ )}
+ >
+ )
+}
+
+export default GridLinkArrow
diff --git a/web/sections/Grid/GridTeaser.tsx b/web/sections/Grid/GridTeaser.tsx
new file mode 100644
index 000000000..d618f8074
--- /dev/null
+++ b/web/sections/Grid/GridTeaser.tsx
@@ -0,0 +1,105 @@
+/* eslint-disable @typescript-eslint/ban-ts-comment */
+import { forwardRef, HTMLAttributes } from 'react'
+import { twMerge } from 'tailwind-merge'
+import Img from 'next/image'
+import { GridTeaserData } from '../../types/types'
+import { urlFor } from '../../common/helpers'
+import { gridContentBlocks } from './GridTextBlock'
+import { RowType } from './mapGridContent'
+import { PortableText } from '@portabletext/react'
+import GridLinkArrow from './GridLinkArrow'
+import { colorKeyToUtilityMap } from '../../styles/colorKeyToUtilityMap'
+import { getColorForTheme } from '../../pageComponents/shared/textTeaser/theme'
+
+export type GridTeaserProps = {
+ data: GridTeaserData
+ rowType?: RowType
+ className?: string
+} & HTMLAttributes
+
+export const GridTeaser = forwardRef(function GridTeaser({ data, rowType }, ref) {
+ const { image, action, content, quote, author, authorTitle, theme, background, imagePosition } = data
+ const imageSrc = urlFor(image).size(1200, 800).auto('format').toString()
+ const altTag = image?.isDecorative ? '' : image?.alt || ''
+ //@ts-ignore
+ const { backgroundUtility, textUtility } = getColorForTheme(theme)
+
+ let bgClassName = ''
+ let textClassName = ''
+ if (theme !== null) {
+ //@ts-ignore
+ bgClassName = colorKeyToUtilityMap[backgroundUtility]?.background
+ //@ts-ignore
+ textClassName = colorKeyToUtilityMap[textUtility]?.text
+ }
+ const aligment = `${imagePosition === 'left' ? '' : '-order-1 text-left'}`
+
+ return (
+
+ {image && (
+
+
+
+ )}
+
+
+
+ {content && (
+
+ )}
+ {quote && (
+
+ )}
+
+ {/*@ts-ignore*/}
+ {action &&
}
+
+
+ )
+})
diff --git a/web/sections/Grid/GridTextBlock.tsx b/web/sections/Grid/GridTextBlock.tsx
new file mode 100644
index 000000000..b29bfdc62
--- /dev/null
+++ b/web/sections/Grid/GridTextBlock.tsx
@@ -0,0 +1,85 @@
+import { twMerge } from 'tailwind-merge'
+import { BlockType } from '../../pageComponents/shared/portableText/helpers/defaultSerializers'
+import { PortableTextBlock } from '@portabletext/types'
+import { PortableText } from '@portabletext/react'
+import { getUrlFromAction } from '../../common/helpers'
+import GridLinkArrow from './GridLinkArrow'
+import { colorKeyToUtilityMap } from '../../styles/colorKeyToUtilityMap'
+import { getColorForTheme } from '../../pageComponents/shared/textTeaser/theme'
+
+type Theme = 'redOnWhite' | 'whiteOnDarkBlue' | 'darkBlueOnLightBlue'
+
+type GridTextBlockProps = {
+ data: any
+ className?: string
+}
+
+/* eslint-disable @typescript-eslint/ban-ts-comment */
+export const gridContentBlocks: BlockType = {
+ //@ts-ignore
+ smallText: ({ children }: PortableTextBlock) => {<>{children}>}
,
+ //@ts-ignore
+ largeText: ({ children }: PortableTextBlock) => {<>{children}>}
,
+ //@ts-ignore
+ extraLargeText: ({ children }: PortableTextBlock) => {
+ return {<>{children}>}
+ },
+ //@ts-ignore
+ normal: ({ children }: PortableTextBlock) => {
+ return (
+
+ <>{children}>
+
+ )
+ },
+}
+/* eslint-enable @typescript-eslint/ban-ts-comment */
+
+const GridTextBlock = ({ data, className }: GridTextBlockProps) => {
+ const { action, content, textAlignment = 'left', theme, backgroundImage } = data
+ const { backgroundUtility, textUtility } = getColorForTheme(theme)
+
+ const url = action && getUrlFromAction(action)
+
+ const contentAlignment = {
+ center: 'justify-center text-center',
+ right: 'justify-center text-start xl:items-end xl:text-end xl:ml-auto',
+ left: 'justify-center text-start xl:items-start xl:mr-auto',
+ }
+
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
+ //@ts-ignore
+ const contentClassNames = twMerge(`${contentAlignment[textAlignment]}`, className)
+ let bgClassName = ''
+ let textClassName = ''
+ if (theme !== null) {
+ //@ts-ignore
+ bgClassName = colorKeyToUtilityMap[backgroundUtility]?.background
+ //@ts-ignore
+ textClassName = colorKeyToUtilityMap[textUtility]?.text
+ }
+
+ return (
+
+
+ {action && url &&
}
+
+ )
+}
+
+export default GridTextBlock
diff --git a/web/sections/Grid/Span2And1.tsx b/web/sections/Grid/Span2And1.tsx
new file mode 100644
index 000000000..9b801e9c4
--- /dev/null
+++ b/web/sections/Grid/Span2And1.tsx
@@ -0,0 +1,30 @@
+import { twMerge } from 'tailwind-merge'
+import { Fragment, HTMLAttributes, forwardRef } from 'react'
+import { mapGridContent } from './mapGridContent'
+
+export type Span2And1Props = {
+ data: any
+ className?: string
+} & HTMLAttributes
+
+const Span2And1 = forwardRef(function Span2And1({ data, className = '' }, ref) {
+ const { singleColumn, span2, alignSpan2Right = false } = data
+ const borderStyling = `shadow-md` //`border border-moss-green-60`
+ return (
+
+ {alignSpan2Right ? (
+ <>
+ {mapGridContent(singleColumn?.content)}
+ {mapGridContent(span2?.content)}
+ >
+ ) : (
+ <>
+ {mapGridContent(span2?.content)}
+ {mapGridContent(singleColumn?.content)}
+ >
+ )}
+
+ )
+})
+
+export default Span2And1
diff --git a/web/sections/Grid/Span3.tsx b/web/sections/Grid/Span3.tsx
new file mode 100644
index 000000000..f051b638d
--- /dev/null
+++ b/web/sections/Grid/Span3.tsx
@@ -0,0 +1,31 @@
+import { twMerge } from 'tailwind-merge'
+import { HTMLAttributes, forwardRef } from 'react'
+import { mapGridContent } from './mapGridContent'
+
+export type Span3Props = {
+ data: any
+ className?: string
+} & HTMLAttributes
+
+const Span3 = forwardRef(function Span3({ data, className = '', ...rest }, ref) {
+ console.log('data?.content', data?.content)
+ return (
+
+ {mapGridContent(data?.content, 'span3')}
+
+ )
+})
+
+export default Span3
diff --git a/web/sections/Grid/ThreeColumns.tsx b/web/sections/Grid/ThreeColumns.tsx
new file mode 100644
index 000000000..f8d99017b
--- /dev/null
+++ b/web/sections/Grid/ThreeColumns.tsx
@@ -0,0 +1,29 @@
+import { twMerge } from 'tailwind-merge'
+import { Fragment, HTMLAttributes, forwardRef } from 'react'
+import { mapGridContent } from './mapGridContent'
+
+export type ThreeColumnsProps = {
+ data: any
+ className?: string
+} & HTMLAttributes
+
+const ThreeColumns = forwardRef(function ThreeColumns(
+ { data, className = '' },
+ ref,
+) {
+ const { columns = [] } = data
+ const borderStyling = `shadow-md` //`border border-moss-green-60`
+ return (
+
+ {columns.map((column: any) => {
+ return (
+
+ {mapGridContent(column)}
+
+ )
+ })}
+
+ )
+})
+
+export default ThreeColumns
diff --git a/web/sections/Grid/index.ts b/web/sections/Grid/index.ts
new file mode 100644
index 000000000..9af6487d1
--- /dev/null
+++ b/web/sections/Grid/index.ts
@@ -0,0 +1,2 @@
+//"use client";
+export { default as Grid, type GridProps } from './Grid'
diff --git a/web/sections/Grid/mapGridContent.tsx b/web/sections/Grid/mapGridContent.tsx
new file mode 100644
index 000000000..322e9d5a0
--- /dev/null
+++ b/web/sections/Grid/mapGridContent.tsx
@@ -0,0 +1,36 @@
+import { ComponentProps } from '../../pageComponents/pageTemplates/shared/SharedPageContent'
+import { FigureData, IFrameData, VideoPlayerData } from '../../types/types'
+import Figure from '../../pageComponents/topicPages/Figure'
+import IFrame from '../../pageComponents/topicPages/IFrame'
+import VideoPlayer from '../../pageComponents/shared/VideoPlayer'
+import GridTextBlock from './GridTextBlock'
+import { GridTeaser } from './GridTeaser'
+import GridFigure from './GridFigure'
+
+export type RowType = 'span3' | 'span2and1' | undefined
+
+export const mapGridContent = (data: ComponentProps, rowType?: RowType): React.ReactNode => {
+ switch (data.type) {
+ case 'gridTextBlock':
+ return
+ case 'gridTeaser':
+ return
+ case 'figure':
+ return
+ case 'iframe':
+ return
+ case 'videoPlayer': {
+ return (
+
+ )
+ }
+ default:
+ return null
+ }
+}
diff --git a/web/tailwind.config.cjs b/web/tailwind.config.cjs
index 7dd956868..620b4cecc 100644
--- a/web/tailwind.config.cjs
+++ b/web/tailwind.config.cjs
@@ -257,6 +257,7 @@ module.exports = {
'layout-sm': 'clamp(16px, calc(-38.3689px + 14.4984vw), 250px)',
'layout-md': 'clamp(16px, calc(-69.4369px + 22.7832vw), 368px)',
'layout-lg': 'clamp(16px, calc(-101.4757px + 31.3269vw), 500px)',
+ 'page-content': 'theme(spacing.16)',
},
keyframes: {
reveal: {
diff --git a/web/twMerge/index.ts b/web/twMerge/index.ts
new file mode 100644
index 000000000..bbbe35a53
--- /dev/null
+++ b/web/twMerge/index.ts
@@ -0,0 +1,17 @@
+import { extendTailwindMerge } from 'tailwind-merge'
+
+const envisTwMerge = extendTailwindMerge({
+ extend: {
+ // ↓ Add values to existing theme scale or create a new one
+ theme: {
+ padding: ['page-content', 'layout-sm', 'layout-md', 'layout-lg'],
+ margin: ['page-content', 'layout-sm', 'layout-md', 'layout-lg'],
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ //@ts-ignore
+ maxWidth: ['viewport'],
+ minWidthh: ['viewport'],
+ },
+ },
+})
+
+export default envisTwMerge
diff --git a/web/types/types.ts b/web/types/types.ts
index 1ab5f0db8..14d29f1c0 100644
--- a/web/types/types.ts
+++ b/web/types/types.ts
@@ -7,6 +7,7 @@ import {
SanityImageSource,
} from '@sanity/image-url/lib/types/types'
import { ColorKeyTokens } from '../styles/colorKeyToUtilityMap'
+import { RowType } from '@sections/Grid/mapGridContent'
export type CaptionData = {
attribution?: string
@@ -225,6 +226,7 @@ export type PageSchema = {
content?: ContentType[]
id: string
type: string
+ isCampaign?: boolean
breadcrumbs: {
enableBreadcrumbs: boolean
useCustomBreadcrumbs: boolean
@@ -828,3 +830,33 @@ export type CardListItemData = {
title?: string
content?: PortableTextBlock[]
}
+
+export type GridData = {
+ type: 'grid'
+ id: string
+ gridRows?: any[]
+}
+
+export type CampaignBannerData = {
+ type: string
+ id: string
+ title: PortableTextBlock[]
+ text: PortableTextBlock[]
+ useLightOverlay?: boolean
+ attribution?: string
+ backgroundImage: SanityImageObject
+ backgroundColor: BackgroundColours
+ backgroundUtility: keyof ColorKeyTokens
+}
+export type GridTeaserData = {
+ image: ImageWithAlt
+ rowType?: RowType
+ content?: PortableTextBlock
+ quote?: string
+ author?: string
+ authorTitle?: string
+ background?: BackgroundColours
+ imagePosition?: TeaserImagePosition
+ action?: LinkData
+ theme?: string
+}