From 6184662caa672460f6d69eebe70465a7612bd9a1 Mon Sep 17 00:00:00 2001 From: Christel Westerberg Date: Thu, 4 Jul 2024 10:33:49 +0200 Subject: [PATCH] fix: add imageContainer reference in rte --- .../ImageContainer/imageContainer.module.css | 23 ++++ components/ImageContainer/index.tsx | 36 +++++ components/JsonToHtml/index.tsx | 1 - components/JsonToHtml/jsontohtml.module.css | 6 +- components/JsonToHtml/renderOptions.tsx | 128 +++++++++++++++--- components/JsonToHtml/utils.tsx | 7 +- lib/graphql/Query/LoyaltyPage.graphql | 15 ++ .../contentstack/loyaltyPage/output.ts | 19 ++- types/components/imageContainer.ts | 6 + types/requests/embeds.ts | 3 +- types/requests/imageContainer.ts | 14 ++ types/requests/rte.ts | 6 + types/requests/utils/embeds.ts | 4 + types/rte/enums.ts | 9 ++ 14 files changed, 252 insertions(+), 25 deletions(-) create mode 100644 components/ImageContainer/imageContainer.module.css create mode 100644 components/ImageContainer/index.tsx create mode 100644 types/components/imageContainer.ts create mode 100644 types/requests/imageContainer.ts create mode 100644 types/requests/rte.ts diff --git a/components/ImageContainer/imageContainer.module.css b/components/ImageContainer/imageContainer.module.css new file mode 100644 index 000000000..bde762010 --- /dev/null +++ b/components/ImageContainer/imageContainer.module.css @@ -0,0 +1,23 @@ +.container { + display: grid; + gap: var(--Spacing-x2); + width: 100%; + grid-template-columns: auto; +} + +.image { + max-width: 100%; + height: 365px; + object-fit: cover; + border-radius: var(--Corner-radius-Medium); +} + +@media screen and (min-width: 768px) { + .container { + grid-template-columns: 1fr 1fr; + } + + .image { + margin: var(--Spacing-x1) var(--Spacing-x0); + } +} diff --git a/components/ImageContainer/index.tsx b/components/ImageContainer/index.tsx new file mode 100644 index 000000000..c7e23d621 --- /dev/null +++ b/components/ImageContainer/index.tsx @@ -0,0 +1,36 @@ +import Image from "../Image" +import Caption from "../TempDesignSystem/Text/Caption" + +import styles from "./imageContainer.module.css" + +import type { ImageContainerProps } from "@/types/components/imageContainer" + +export default function ImageContainer({ + leftImage, + rightImage, +}: ImageContainerProps) { + return ( +
+
+ {leftImage.meta.alt + {leftImage.meta.caption} +
+
+ {rightImage.meta.alt + {leftImage.meta.caption} +
+
+ ) +} diff --git a/components/JsonToHtml/index.tsx b/components/JsonToHtml/index.tsx index 689bd0460..48ab55c93 100644 --- a/components/JsonToHtml/index.tsx +++ b/components/JsonToHtml/index.tsx @@ -10,7 +10,6 @@ export default function JsonToHtml({ if (!Array.isArray(nodes) || !nodes.length) { return null } - console.log({ nodes }) return (
{nodesToHtml(nodes, embeds, renderOptions).filter(Boolean)} diff --git a/components/JsonToHtml/jsontohtml.module.css b/components/JsonToHtml/jsontohtml.module.css index 178802905..25263c850 100644 --- a/components/JsonToHtml/jsontohtml.module.css +++ b/components/JsonToHtml/jsontohtml.module.css @@ -4,5 +4,9 @@ height: 365px; object-fit: cover; border-radius: var(--Corner-radius-Medium); - padding: var(--Spacing-x1) var(--Spacing-x0); + margin: var(--Spacing-x1) var(--Spacing-x0); +} + +.li { + margin-left: var(--Spacing-x3); } diff --git a/components/JsonToHtml/renderOptions.tsx b/components/JsonToHtml/renderOptions.tsx index cd748e58e..70cd1f40a 100644 --- a/components/JsonToHtml/renderOptions.tsx +++ b/components/JsonToHtml/renderOptions.tsx @@ -2,25 +2,33 @@ import Image from "@/components/Image" import Link from "@/components/TempDesignSystem/Link" import { insertResponseToImageVaultAsset } from "@/utils/imageVault" +import ImageContainer from "../ImageContainer" import Divider from "../TempDesignSystem/Divider" import BiroScript from "../TempDesignSystem/Text/BiroScript" import Body from "../TempDesignSystem/Text/Body" import Caption from "../TempDesignSystem/Text/Caption" +import Footnote from "../TempDesignSystem/Text/Footnote" import Subtitle from "../TempDesignSystem/Text/Subtitle" import Title from "../TempDesignSystem/Text/Title" +import { hasAvailableFormat } from "./utils" import styles from "./jsontohtml.module.css" import type { EmbedByUid } from "@/types/components/jsontohtml" import { EmbedEnum } from "@/types/requests/utils/embeds" import type { Attributes, RTEImageVaultAttrs } from "@/types/rte/attrs" -import { RTEItemTypeEnum, RTETypeEnum } from "@/types/rte/enums" +import { + AvailableFormatEnum, + RTEItemTypeEnum, + RTETypeEnum, +} from "@/types/rte/enums" import type { RTEDefaultNode, RTEImageNode, RTENext, RTENode, RTERegularNode, + RTETextNode, } from "@/types/rte/node" import { RTEMarkType } from "@/types/rte/node" import type { RenderOptions } from "@/types/rte/option" @@ -57,14 +65,16 @@ export const renderOptions: RenderOptions = { if (node.attrs.url) { const props = extractPossibleAttributes(node.attrs) return ( - {next(node.children, embeds, fullRenderOptions)} - + ) } return null @@ -217,7 +227,7 @@ export const renderOptions: RenderOptions = { ) => { const props = extractPossibleAttributes(node.attrs) return ( -
  • +
  • {next(node.children, embeds, fullRenderOptions)}
  • ) @@ -244,6 +254,21 @@ export const renderOptions: RenderOptions = { fullRenderOptions: RenderOptions ) => { const props = extractPossibleAttributes(node.attrs) + + const hasFormat = node.children.some((item) => + hasAvailableFormat((item as RTETextNode).classname) + ) + + // If a child node has an available format as className, we wrap it in a + // span and render the children with the correct component + if (hasFormat) { + return ( + + {next(node.children, embeds, fullRenderOptions)} + + ) + } + return ( {next(node.children, embeds, fullRenderOptions)} @@ -264,11 +289,11 @@ export const renderOptions: RenderOptions = { if (image?.node.__typename === EmbedEnum.SysAsset) { const alt = image?.node?.title ?? node.attrs.alt const props = extractPossibleAttributes(node.attrs) + props.className = styles.image return ( {alt} ) } - } else { - const props = extractPossibleAttributes(node.attrs) - const href = node.attrs?.locale - ? `/${node.attrs.locale}${node.attrs.href}` - : node.attrs.href - return ( - - {next(node.children, embeds, fullRenderOptions)} - - ) + } else if (type === RTEItemTypeEnum.entry) { + const entry = embeds?.[node?.attrs?.["entry-uid"]] + + if (entry?.node.__typename === EmbedEnum.ImageContainer) { + const leftImage = insertResponseToImageVaultAsset( + entry.node.image_left + ) + const rightImage = insertResponseToImageVaultAsset( + entry.node.image_right + ) + return ( + + ) + } else { + // If entry is not an ImageContainer, it is a page and we return it as a link + const props = extractPossibleAttributes(node.attrs) + const href = node.attrs?.locale + ? `/${node.attrs.locale}${node.attrs.href}` + : node.attrs.href + return ( + + {next(node.children, embeds, fullRenderOptions)} + + ) + } } } @@ -300,7 +346,6 @@ export const renderOptions: RenderOptions = { const image = insertResponseToImageVaultAsset(attrs) const alt = image.meta.alt ?? image.title - const height = parseInt(attrs.height.replaceAll("px", "")) const width = parseInt(attrs.width.replaceAll("px", "")) const props = extractPossibleAttributes(attrs) return ( @@ -308,7 +353,7 @@ export const renderOptions: RenderOptions = { {alt} + {children} + + ) + } + + if (className === AvailableFormatEnum.caption) { + return ( + + {children} + + ) + } + + if (className === AvailableFormatEnum["script-1"]) { + return ( + + {children} + + ) + } + + if (className === AvailableFormatEnum["script-2"]) { + return ( + + {children} + + ) + } + + if (className === AvailableFormatEnum["subtitle-1"]) { + return ( + + {children} + + ) + } + if (className === AvailableFormatEnum["subtitle-2"]) { + return ( + + {children} + + ) + } return ( {children} diff --git a/components/JsonToHtml/utils.tsx b/components/JsonToHtml/utils.tsx index f368e380c..bf798829b 100644 --- a/components/JsonToHtml/utils.tsx +++ b/components/JsonToHtml/utils.tsx @@ -3,7 +3,7 @@ import { renderOptions } from "./renderOptions" import type { EmbedByUid } from "@/types/components/jsontohtml" import type { Embeds } from "@/types/requests/embeds" import type { Node } from "@/types/requests/utils/edges" -import { RTETypeEnum } from "@/types/rte/enums" +import { AvailableFormatEnum, RTETypeEnum } from "@/types/rte/enums" import type { RTENode, RTERenderOptionComponent, @@ -74,7 +74,6 @@ export function textNodeToHtml( if (node.bold) { text = (fullRenderOptions[RTEMarkType.bold] as RTERenderMark)(text) } - return text } @@ -86,6 +85,10 @@ function next( return nodeChildrenToHtml(nodes, embeds, fullRenderOptions) } +export function hasAvailableFormat(className?: string) { + return className && Object.keys(AvailableFormatEnum).includes(className) +} + export function nodeToHtml( node: RTENode, embeds: EmbedByUid, diff --git a/lib/graphql/Query/LoyaltyPage.graphql b/lib/graphql/Query/LoyaltyPage.graphql index 25a577afb..5ef8479f8 100644 --- a/lib/graphql/Query/LoyaltyPage.graphql +++ b/lib/graphql/Query/LoyaltyPage.graphql @@ -70,6 +70,15 @@ query GetLoyaltyPage($locale: String!, $uid: String!) { __typename ...LoyaltyPageLink ...ContentPageLink + ...Image + ... on ImageContainer { + title + image_left + image_right + system { + uid + } + } } } totalCount @@ -223,6 +232,12 @@ query GetLoyaltyPageRefs($locale: String!, $uid: String!) { ...System } } + ... on ImageContainer { + __typename + system { + ...System + } + } } } } diff --git a/server/routers/contentstack/loyaltyPage/output.ts b/server/routers/contentstack/loyaltyPage/output.ts index 0f2196b76..8c1374ff2 100644 --- a/server/routers/contentstack/loyaltyPage/output.ts +++ b/server/routers/contentstack/loyaltyPage/output.ts @@ -13,6 +13,7 @@ import { } from "@/types/components/loyalty/enums" import { Embeds } from "@/types/requests/embeds" import { PageLinkEnum } from "@/types/requests/pageLinks" +import { RTEEmbedsEnum } from "@/types/requests/rte" import { EdgesWithTotalCount } from "@/types/requests/utils/edges" import { RTEDocument } from "@/types/rte/node" @@ -272,6 +273,20 @@ const pageConnectionRefs = z.object({ ), }) +const rteConnectionRefs = z.object({ + edges: z.array( + z.object({ + node: z.object({ + __typename: z.nativeEnum(RTEEmbedsEnum), + system: z.object({ + content_type_uid: z.string(), + uid: z.string(), + }), + }), + }) + ), +}) + const cardBlockRefs = z.object({ __typename: z.literal(LoyaltyCardsGridEnum.Card), primary_button: z @@ -349,7 +364,7 @@ const loyaltyPageBlockTextContentRefs = z.object({ __typename: z.literal(LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksContent), content: z.object({ content: z.object({ - embedded_itemsConnection: pageConnectionRefs, + embedded_itemsConnection: rteConnectionRefs, }), }), }) @@ -365,7 +380,7 @@ const loyaltyPageSidebarTextContentRef = z.object({ __typename: z.literal(SidebarTypenameEnum.LoyaltyPageSidebarContent), content: z.object({ content: z.object({ - embedded_itemsConnection: pageConnectionRefs, + embedded_itemsConnection: rteConnectionRefs, }), }), }) diff --git a/types/components/imageContainer.ts b/types/components/imageContainer.ts new file mode 100644 index 000000000..6f8e75393 --- /dev/null +++ b/types/components/imageContainer.ts @@ -0,0 +1,6 @@ +import type { ImageVaultAsset } from "./imageVaultImage" + +export type ImageContainerProps = { + leftImage: ImageVaultAsset + rightImage: ImageVaultAsset +} diff --git a/types/requests/embeds.ts b/types/requests/embeds.ts index 11e863fd5..714c8746f 100644 --- a/types/requests/embeds.ts +++ b/types/requests/embeds.ts @@ -1,3 +1,4 @@ +import type { ImageContainer } from "./imageContainer" import type { SysAsset } from "./utils/asset" -export type Embeds = SysAsset +export type Embeds = SysAsset | ImageContainer diff --git a/types/requests/imageContainer.ts b/types/requests/imageContainer.ts new file mode 100644 index 000000000..3eb85023c --- /dev/null +++ b/types/requests/imageContainer.ts @@ -0,0 +1,14 @@ +import { InsertResponse } from "../components/imageVaultImage" +import { EmbedEnum } from "./utils/embeds" +import { Typename } from "./utils/typename" + +export type ImageContainer = Typename< + { + image_left: InsertResponse + image_right: InsertResponse + system: { + uid: string + } + }, + EmbedEnum.ImageContainer +> diff --git a/types/requests/rte.ts b/types/requests/rte.ts new file mode 100644 index 000000000..f6e18b41a --- /dev/null +++ b/types/requests/rte.ts @@ -0,0 +1,6 @@ +export enum RTEEmbedsEnum { + AccountPage = "AccountPage", + ContentPage = "ContentPage", + LoyaltyPage = "LoyaltyPage", + ImageContainer = "ImageContainer", +} diff --git a/types/requests/utils/embeds.ts b/types/requests/utils/embeds.ts index 8cdb3fcaa..527564882 100644 --- a/types/requests/utils/embeds.ts +++ b/types/requests/utils/embeds.ts @@ -1,6 +1,10 @@ export enum EmbedEnum { CurrentBlocksPage = "CurrentBlocksPage", SysAsset = "SysAsset", + ImageContainer = "ImageContainer", + LoyaltyPage = "LoyaltyPage", + AccountPage = "AccountPage", + ContentPage = "ContentPage", } export type Embed = keyof typeof EmbedEnum diff --git a/types/rte/enums.ts b/types/rte/enums.ts index 271c800aa..feb7834da 100644 --- a/types/rte/enums.ts +++ b/types/rte/enums.ts @@ -47,3 +47,12 @@ export enum RTEItemTypeEnum { } export type RTEItemType = keyof typeof RTEItemTypeEnum + +export enum AvailableFormatEnum { + "script-1" = "script-1", + "script-2" = "script-2", + "footnote" = "footnote", + "caption" = "caption", + "subtitle-1" = "subtitle-1", + "subtitle-2" = "subtitle-2", +}