feat(SW-285): Ship support for ContentPageBlocksCardsGrid
This commit is contained in:
52
components/Content/Blocks/CardsGrid/index.tsx
Normal file
52
components/Content/Blocks/CardsGrid/index.tsx
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import SectionContainer from "@/components/Section/Container"
|
||||||
|
import SectionHeader from "@/components/Section/Header"
|
||||||
|
import Card from "@/components/TempDesignSystem/Card"
|
||||||
|
import Grids from "@/components/TempDesignSystem/Grids"
|
||||||
|
import LoyaltyCard from "@/components/TempDesignSystem/LoyaltyCard"
|
||||||
|
|
||||||
|
import { CardsGridProps } from "@/types/components/content/blocks"
|
||||||
|
import { CardsGridEnum } from "@/types/components/content/enums"
|
||||||
|
|
||||||
|
export default function CardsGrid({
|
||||||
|
cards_grid,
|
||||||
|
firstItem = false,
|
||||||
|
}: CardsGridProps) {
|
||||||
|
return (
|
||||||
|
<SectionContainer>
|
||||||
|
<SectionHeader
|
||||||
|
title={cards_grid.title}
|
||||||
|
subtitle={cards_grid.preamble}
|
||||||
|
topTitle={firstItem}
|
||||||
|
/>
|
||||||
|
<Grids.Stackable>
|
||||||
|
{cards_grid.cards.map((card) => {
|
||||||
|
switch (card.__typename) {
|
||||||
|
case CardsGridEnum.Card: {
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
theme={cards_grid.theme || "one"}
|
||||||
|
key={card.system.uid}
|
||||||
|
scriptedTopTitle={card.scripted_top_title}
|
||||||
|
heading={card.heading}
|
||||||
|
bodyText={card.body_text}
|
||||||
|
secondaryButton={card.secondaryButton}
|
||||||
|
primaryButton={card.primaryButton}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case CardsGridEnum.LoyaltyCard:
|
||||||
|
return (
|
||||||
|
<LoyaltyCard
|
||||||
|
key={card.system.uid}
|
||||||
|
image={card.image}
|
||||||
|
heading={card.heading}
|
||||||
|
bodyText={card.body_text}
|
||||||
|
link={card.link}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</Grids.Stackable>
|
||||||
|
</SectionContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -2,7 +2,8 @@ import JsonToHtml from "@/components/JsonToHtml"
|
|||||||
// import DynamicContentBlock from "@/components/Loyalty/Blocks/DynamicContent"
|
// import DynamicContentBlock from "@/components/Loyalty/Blocks/DynamicContent"
|
||||||
import Shortcuts from "@/components/MyPages/Blocks/Shortcuts"
|
import Shortcuts from "@/components/MyPages/Blocks/Shortcuts"
|
||||||
|
|
||||||
// import CardsGrid from "./CardsGrid"
|
import CardsGrid from "./CardsGrid"
|
||||||
|
|
||||||
import type { BlocksProps } from "@/types/components/content/blocks"
|
import type { BlocksProps } from "@/types/components/content/blocks"
|
||||||
import { ContentBlocksTypenameEnum } from "@/types/components/content/enums"
|
import { ContentBlocksTypenameEnum } from "@/types/components/content/enums"
|
||||||
|
|
||||||
@@ -29,6 +30,14 @@ export function Blocks({ blocks }: BlocksProps) {
|
|||||||
title={block.shortcuts.title}
|
title={block.shortcuts.title}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
case ContentBlocksTypenameEnum.ContentPageBlocksCardsGrid:
|
||||||
|
return (
|
||||||
|
<CardsGrid
|
||||||
|
cards_grid={block.cards_grid}
|
||||||
|
key={`${block.cards_grid.title}-${idx}`}
|
||||||
|
firstItem={firstItem}
|
||||||
|
/>
|
||||||
|
)
|
||||||
default:
|
default:
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
#import "../Fragments/Blocks/Card.graphql"
|
||||||
|
#import "../Fragments/Blocks/LoyaltyCard.graphql"
|
||||||
|
#import "../Fragments/Blocks/Refs/Card.graphql"
|
||||||
|
#import "../Fragments/Blocks/Refs/LoyaltyCard.graphql"
|
||||||
|
|
||||||
#import "../Fragments/PageLink/AccountPageLink.graphql"
|
#import "../Fragments/PageLink/AccountPageLink.graphql"
|
||||||
#import "../Fragments/PageLink/ContentPageLink.graphql"
|
#import "../Fragments/PageLink/ContentPageLink.graphql"
|
||||||
#import "../Fragments/PageLink/LoyaltyPageLink.graphql"
|
#import "../Fragments/PageLink/LoyaltyPageLink.graphql"
|
||||||
@@ -60,6 +65,24 @@ query GetContentPage($locale: String!, $uid: String!) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
... on ContentPageBlocksCardsGrid {
|
||||||
|
__typename
|
||||||
|
cards_grid {
|
||||||
|
title
|
||||||
|
preamble
|
||||||
|
layout
|
||||||
|
theme
|
||||||
|
cardConnection(limit: 10) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
__typename
|
||||||
|
...CardBlock
|
||||||
|
...LoyaltyCardBlock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
title
|
title
|
||||||
header {
|
header {
|
||||||
@@ -128,6 +151,19 @@ query GetContentPageRefs($locale: String!, $uid: String!) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
... on ContentPageBlocksCardsGrid {
|
||||||
|
__typename
|
||||||
|
cards_grid {
|
||||||
|
cardConnection(limit: 10) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
...CardBlockRef
|
||||||
|
...LoyaltyCardBlockRef
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
system {
|
system {
|
||||||
...System
|
...System
|
||||||
|
|||||||
@@ -4,9 +4,13 @@ import { Lang } from "@/constants/languages"
|
|||||||
|
|
||||||
import { imageVaultAssetSchema } from "../schemas/imageVault"
|
import { imageVaultAssetSchema } from "../schemas/imageVault"
|
||||||
|
|
||||||
import { ContentBlocksTypenameEnum } from "@/types/components/content/enums"
|
import {
|
||||||
|
CardsGridEnum,
|
||||||
|
ContentBlocksTypenameEnum,
|
||||||
|
} from "@/types/components/content/enums"
|
||||||
import { ImageVaultAsset } from "@/types/components/imageVault"
|
import { ImageVaultAsset } from "@/types/components/imageVault"
|
||||||
import { Embeds } from "@/types/requests/embeds"
|
import { Embeds } from "@/types/requests/embeds"
|
||||||
|
import { PageLinkEnum } from "@/types/requests/pageLinks"
|
||||||
import { RTEEmbedsEnum } from "@/types/requests/rte"
|
import { RTEEmbedsEnum } from "@/types/requests/rte"
|
||||||
import { EdgesWithTotalCount } from "@/types/requests/utils/edges"
|
import { EdgesWithTotalCount } from "@/types/requests/utils/edges"
|
||||||
import { RTEDocument } from "@/types/rte/node"
|
import { RTEDocument } from "@/types/rte/node"
|
||||||
@@ -41,9 +45,74 @@ const contentPageShortcuts = z.object({
|
|||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// TODO: this is a separate entity, should be in a separate file.
|
||||||
|
const cardBlock = z.object({
|
||||||
|
__typename: z.literal(CardsGridEnum.Card),
|
||||||
|
heading: z.string().nullable(),
|
||||||
|
body_text: z.string().nullable(),
|
||||||
|
background_image: z.any(),
|
||||||
|
scripted_top_title: z.string().nullable(),
|
||||||
|
primaryButton: z
|
||||||
|
.object({
|
||||||
|
openInNewTab: z.boolean(),
|
||||||
|
title: z.string(),
|
||||||
|
href: z.string(),
|
||||||
|
isExternal: z.boolean(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
secondaryButton: z
|
||||||
|
.object({
|
||||||
|
openInNewTab: z.boolean(),
|
||||||
|
title: z.string(),
|
||||||
|
href: z.string(),
|
||||||
|
isExternal: z.boolean(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
system: z.object({
|
||||||
|
locale: z.nativeEnum(Lang),
|
||||||
|
uid: z.string(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const loyaltyCardBlock = z.object({
|
||||||
|
__typename: z.literal(CardsGridEnum.LoyaltyCard),
|
||||||
|
heading: z.string().nullable(),
|
||||||
|
body_text: z.string().nullable(),
|
||||||
|
image: z.any(),
|
||||||
|
link: z
|
||||||
|
.object({
|
||||||
|
openInNewTab: z.boolean(),
|
||||||
|
title: z.string(),
|
||||||
|
href: z.string(),
|
||||||
|
isExternal: z.boolean(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
system: z.object({
|
||||||
|
locale: z.nativeEnum(Lang),
|
||||||
|
uid: z.string(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const contentPageCardsItems = z.discriminatedUnion("__typename", [
|
||||||
|
loyaltyCardBlock,
|
||||||
|
cardBlock,
|
||||||
|
])
|
||||||
|
|
||||||
|
const contentPageCards = z.object({
|
||||||
|
__typename: z.literal(ContentBlocksTypenameEnum.ContentPageBlocksCardsGrid),
|
||||||
|
cards_grid: z.object({
|
||||||
|
title: z.string().nullable(),
|
||||||
|
preamble: z.string().nullable(),
|
||||||
|
layout: z.enum(["twoColumnGrid", "threeColumnGrid", "twoPlusOne"]),
|
||||||
|
theme: z.enum(["one", "two", "three"]).nullable(),
|
||||||
|
cards: z.array(contentPageCardsItems),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
const contentPageBlockItem = z.discriminatedUnion("__typename", [
|
const contentPageBlockItem = z.discriminatedUnion("__typename", [
|
||||||
contentPageBlockTextContent,
|
contentPageBlockTextContent,
|
||||||
contentPageShortcuts,
|
contentPageShortcuts,
|
||||||
|
contentPageCards,
|
||||||
])
|
])
|
||||||
|
|
||||||
type BlockContentRaw = z.infer<typeof contentPageBlockTextContent>
|
type BlockContentRaw = z.infer<typeof contentPageBlockTextContent>
|
||||||
@@ -57,7 +126,22 @@ export interface RteBlockContent extends BlockContentRaw {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type Shortcuts = z.infer<typeof contentPageShortcuts>
|
export type Shortcuts = z.infer<typeof contentPageShortcuts>
|
||||||
export type Block = RteBlockContent | Shortcuts
|
|
||||||
|
type LoyaltyCardRaw = z.infer<typeof loyaltyCardBlock>
|
||||||
|
type LoyaltyCard = Omit<LoyaltyCardRaw, "image"> & {
|
||||||
|
image?: ImageVaultAsset
|
||||||
|
}
|
||||||
|
type CardRaw = z.infer<typeof cardBlock>
|
||||||
|
type Card = Omit<CardRaw, "background_image"> & {
|
||||||
|
backgroundImage?: ImageVaultAsset
|
||||||
|
}
|
||||||
|
type CardsGridRaw = z.infer<typeof contentPageCards>
|
||||||
|
export type CardsGrid = Omit<CardsGridRaw, "cards"> & {
|
||||||
|
cards: (LoyaltyCard | Card)[]
|
||||||
|
}
|
||||||
|
export type CardsRaw = CardsGrid["cards_grid"]["cards"][number]
|
||||||
|
|
||||||
|
export type Block = RteBlockContent | Shortcuts | CardsGrid
|
||||||
|
|
||||||
// Content Page Schema and types
|
// Content Page Schema and types
|
||||||
export const validateContentPageSchema = z.object({
|
export const validateContentPageSchema = z.object({
|
||||||
@@ -86,6 +170,20 @@ export type ContentPage = Omit<ContentPageRaw, "blocks" | "hero_image"> & {
|
|||||||
blocks: Block[]
|
blocks: Block[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pageConnectionRefs = z.object({
|
||||||
|
edges: z.array(
|
||||||
|
z.object({
|
||||||
|
node: z.object({
|
||||||
|
__typename: z.nativeEnum(PageLinkEnum),
|
||||||
|
system: z.object({
|
||||||
|
content_type_uid: z.string(),
|
||||||
|
uid: z.string(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
})
|
||||||
|
|
||||||
const rteConnectionRefs = z.object({
|
const rteConnectionRefs = z.object({
|
||||||
edges: z.array(
|
edges: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
@@ -100,6 +198,42 @@ const rteConnectionRefs = z.object({
|
|||||||
),
|
),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const cardBlockRefs = z.object({
|
||||||
|
__typename: z.literal(CardsGridEnum.Card),
|
||||||
|
primary_button: z
|
||||||
|
.object({
|
||||||
|
linkConnection: pageConnectionRefs,
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
secondary_button: z
|
||||||
|
.object({
|
||||||
|
linkConnection: pageConnectionRefs,
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
system: z.object({
|
||||||
|
content_type_uid: z.string(),
|
||||||
|
uid: z.string(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const loyaltyCardBlockRefs = z.object({
|
||||||
|
__typename: z.literal(CardsGridEnum.LoyaltyCard),
|
||||||
|
link: z
|
||||||
|
.object({
|
||||||
|
linkConnection: pageConnectionRefs,
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
system: z.object({
|
||||||
|
content_type_uid: z.string(),
|
||||||
|
uid: z.string(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const cardGridCardsRef = z.discriminatedUnion("__typename", [
|
||||||
|
loyaltyCardBlockRefs,
|
||||||
|
cardBlockRefs,
|
||||||
|
])
|
||||||
|
|
||||||
const contentPageBlockTextContentRefs = z.object({
|
const contentPageBlockTextContentRefs = z.object({
|
||||||
__typename: z.literal(ContentBlocksTypenameEnum.ContentPageBlocksContent),
|
__typename: z.literal(ContentBlocksTypenameEnum.ContentPageBlocksContent),
|
||||||
content: z.object({
|
content: z.object({
|
||||||
@@ -109,6 +243,13 @@ const contentPageBlockTextContentRefs = z.object({
|
|||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const contentPageCardsRefs = z.object({
|
||||||
|
__typename: z.literal(ContentBlocksTypenameEnum.ContentPageBlocksCardsGrid),
|
||||||
|
cards_grid: z.object({
|
||||||
|
cardConnection: cardGridCardsRef,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
const contentPageShortcutsRefs = z.object({
|
const contentPageShortcutsRefs = z.object({
|
||||||
__typename: z.literal(ContentBlocksTypenameEnum.ContentPageBlocksShortcuts),
|
__typename: z.literal(ContentBlocksTypenameEnum.ContentPageBlocksShortcuts),
|
||||||
shortcuts: z.object({
|
shortcuts: z.object({
|
||||||
@@ -123,6 +264,7 @@ const contentPageShortcutsRefs = z.object({
|
|||||||
const contentPageBlockRefsItem = z.discriminatedUnion("__typename", [
|
const contentPageBlockRefsItem = z.discriminatedUnion("__typename", [
|
||||||
contentPageBlockTextContentRefs,
|
contentPageBlockTextContentRefs,
|
||||||
contentPageShortcutsRefs,
|
contentPageShortcutsRefs,
|
||||||
|
contentPageCardsRefs,
|
||||||
])
|
])
|
||||||
|
|
||||||
export const validateContentPageRefsSchema = z.object({
|
export const validateContentPageRefsSchema = z.object({
|
||||||
|
|||||||
@@ -18,10 +18,14 @@ import {
|
|||||||
fetchContentPageRefs,
|
fetchContentPageRefs,
|
||||||
generatePageTags,
|
generatePageTags,
|
||||||
getContentPageCounter,
|
getContentPageCounter,
|
||||||
|
makeButtonObject,
|
||||||
validateContentPageRefs,
|
validateContentPageRefs,
|
||||||
} from "./utils"
|
} from "./utils"
|
||||||
|
|
||||||
import { ContentBlocksTypenameEnum } from "@/types/components/content/enums"
|
import {
|
||||||
|
CardsGridEnum,
|
||||||
|
ContentBlocksTypenameEnum,
|
||||||
|
} from "@/types/components/content/enums"
|
||||||
import {
|
import {
|
||||||
TrackingChannelEnum,
|
TrackingChannelEnum,
|
||||||
TrackingSDKPageData,
|
TrackingSDKPageData,
|
||||||
@@ -85,6 +89,38 @@ export const contentPageQueryRouter = router({
|
|||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
case ContentBlocksTypenameEnum.ContentPageBlocksCardsGrid:
|
||||||
|
return {
|
||||||
|
...block,
|
||||||
|
cards_grid: {
|
||||||
|
...block.cards_grid,
|
||||||
|
cards: block.cards_grid.cardConnection.edges.map(
|
||||||
|
({ node: card }: { node: any }) => {
|
||||||
|
switch (card.__typename) {
|
||||||
|
case CardsGridEnum.Card:
|
||||||
|
return {
|
||||||
|
...card,
|
||||||
|
backgroundImage: makeImageVaultImage(
|
||||||
|
card.background_image
|
||||||
|
),
|
||||||
|
primaryButton: card.has_primary_button
|
||||||
|
? makeButtonObject(card.primary_button)
|
||||||
|
: undefined,
|
||||||
|
secondaryButton: card.has_secondary_button
|
||||||
|
? makeButtonObject(card.secondary_button)
|
||||||
|
: undefined,
|
||||||
|
}
|
||||||
|
case CardsGridEnum.LoyaltyCard:
|
||||||
|
return {
|
||||||
|
...card,
|
||||||
|
image: makeImageVaultImage(card.image),
|
||||||
|
link: makeButtonObject(card.link),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return block
|
return block
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { request } from "@/lib/graphql/request"
|
|||||||
import { notFound } from "@/server/errors/trpc"
|
import { notFound } from "@/server/errors/trpc"
|
||||||
|
|
||||||
import { generateTag, generateTags } from "@/utils/generateTag"
|
import { generateTag, generateTags } from "@/utils/generateTag"
|
||||||
|
import { removeMultipleSlashes } from "@/utils/url"
|
||||||
|
|
||||||
import { removeEmptyObjects } from "../../utils"
|
import { removeEmptyObjects } from "../../utils"
|
||||||
import { ContentPageRefsDataRaw, validateContentPageRefsSchema } from "./output"
|
import { ContentPageRefsDataRaw, validateContentPageRefsSchema } from "./output"
|
||||||
@@ -130,3 +131,29 @@ export function getConnections(refs: ContentPageRefsDataRaw) {
|
|||||||
}
|
}
|
||||||
return connections
|
return connections
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function makeButtonObject(button: any) {
|
||||||
|
if (!button) return null
|
||||||
|
|
||||||
|
const isContenstackLink =
|
||||||
|
button?.is_contentstack_link || button.linkConnection?.edges?.length
|
||||||
|
const linkConnnectionNode = isContenstackLink
|
||||||
|
? button.linkConnection.edges[0]?.node
|
||||||
|
: null
|
||||||
|
|
||||||
|
return {
|
||||||
|
openInNewTab: button?.open_in_new_tab,
|
||||||
|
title:
|
||||||
|
button.cta_text ||
|
||||||
|
(linkConnnectionNode
|
||||||
|
? linkConnnectionNode.title
|
||||||
|
: button.external_link.title),
|
||||||
|
href: linkConnnectionNode
|
||||||
|
? linkConnnectionNode.web?.original_url ||
|
||||||
|
removeMultipleSlashes(
|
||||||
|
`/${linkConnnectionNode.system.locale}/${linkConnnectionNode.url}`
|
||||||
|
)
|
||||||
|
: button.external_link.href,
|
||||||
|
isExternal: !isContenstackLink,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
import { Block } from "@/server/routers/contentstack/contentPage/output"
|
import {
|
||||||
|
Block,
|
||||||
|
CardsGrid,
|
||||||
|
} from "@/server/routers/contentstack/contentPage/output"
|
||||||
|
|
||||||
export type BlocksProps = {
|
export type BlocksProps = {
|
||||||
blocks: Block[]
|
blocks: Block[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type CardsGridProps = Pick<CardsGrid, "cards_grid"> & {
|
||||||
|
firstItem?: boolean
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
export enum ContentBlocksTypenameEnum {
|
export enum ContentBlocksTypenameEnum {
|
||||||
ContentPageBlocksContent = "ContentPageBlocksContent",
|
ContentPageBlocksContent = "ContentPageBlocksContent",
|
||||||
ContentPageBlocksShortcuts = "ContentPageBlocksShortcuts",
|
ContentPageBlocksShortcuts = "ContentPageBlocksShortcuts",
|
||||||
|
ContentPageBlocksCardsGrid = "ContentPageBlocksCardsGrid",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum CardsGridEnum {
|
||||||
|
LoyaltyCard = "LoyaltyCard",
|
||||||
|
Card = "Card",
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user