diff --git a/app/globals.css b/app/globals.css index 67624cbcf..5ec453db0 100644 --- a/app/globals.css +++ b/app/globals.css @@ -123,6 +123,7 @@ html, body { margin: 0; padding: 0; + scroll-behavior: smooth; } body { @@ -130,6 +131,16 @@ body { overflow-x: hidden; } +body.overflow-hidden { + overflow: hidden; +} +@media screen and (min-width: 768px) { + body.overflow-hidden { + overflow: auto; + overflow-x: hidden; + } +} + ul { padding-inline-start: 0; margin-block-start: 0; diff --git a/components/Content/Blocks/TextCols/index.tsx b/components/Content/Blocks/TextCols/index.tsx new file mode 100644 index 000000000..afcc6d0cf --- /dev/null +++ b/components/Content/Blocks/TextCols/index.tsx @@ -0,0 +1,29 @@ +import JsonToHtml from "@/components/JsonToHtml" +import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" + +import { renderOptions } from "./renderOptions" + +import styles from "./textcols.module.css" + +import type { TextColsProps } from "@/types/components/content/blocks" + +export default function TextCols({ textCols }: TextColsProps) { + return ( +
+ {textCols.columns.map((col) => { + return ( +
+ {col.title} +
+ +
+
+ ) + })} +
+ ) +} diff --git a/components/Content/Blocks/TextCols/renderOptions.tsx b/components/Content/Blocks/TextCols/renderOptions.tsx new file mode 100644 index 000000000..55582de95 --- /dev/null +++ b/components/Content/Blocks/TextCols/renderOptions.tsx @@ -0,0 +1,70 @@ +import Link from "@/components/TempDesignSystem/Link" + +import styles from "./textcols.module.css" + +import type { EmbedByUid } from "@/types/components/jsontohtml" +import { RTEItemTypeEnum, RTETypeEnum } from "@/types/rte/enums" +import type { + RTEDefaultNode, + RTENext, + RTENode, + RTERegularNode, +} from "@/types/rte/node" +import type { RenderOptions } from "@/types/rte/option" + +export const renderOptions: RenderOptions = { + [RTETypeEnum.p]: ( + node: RTEDefaultNode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + return ( +

+ {next(node.children, embeds, fullRenderOptions)} +

+ ) + }, + [RTETypeEnum.a]: ( + node: RTERegularNode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + if (node.attrs.url) { + return ( + + {next(node.children, embeds, fullRenderOptions)} + + ) + } + return null + }, + [RTETypeEnum.reference]: ( + node: RTENode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + if ("attrs" in node) { + const type = node.attrs.type + if (type !== RTEItemTypeEnum.asset) { + const href = node.attrs?.locale + ? `/${node.attrs.locale}${node.attrs.href}` + : node.attrs.href + return ( + + {next(node.children, embeds, fullRenderOptions)} + + ) + } + + return null + } + }, +} diff --git a/components/Content/Blocks/TextCols/textcols.module.css b/components/Content/Blocks/TextCols/textcols.module.css new file mode 100644 index 000000000..b3e9e2824 --- /dev/null +++ b/components/Content/Blocks/TextCols/textcols.module.css @@ -0,0 +1,40 @@ +.columns { + display: flex; + flex-direction: column; + gap: var(--Spacing-x3); + padding: var(--Spacing-x3) var(--Spacing-x4); +} + +.column { + padding-bottom: var(--Spacing-x2); + border-bottom: 1px solid var(--Base-Border-Subtle); + gap: var(--Spacing-x1); + display: flex; + flex-direction: column; +} + +.p { + color: var(--UI-Text-High-contrast); + line-height: var(--Spacing-x3); + margin: 0; +} + +.a { + color: var(--Base-Text-High-contrast); +} + +.text > section { + gap: 0; +} + +@media (min-width: 768px) { + .columns { + flex-direction: row; + flex-wrap: wrap; + } + + .column { + flex: 0 0 calc(50% - var(--Spacing-x3)); + max-width: calc(50% - var(--Spacing-x3)); + } +} diff --git a/components/Content/Blocks/index.tsx b/components/Content/Blocks/index.tsx index 9c396ed11..77dabdfd1 100644 --- a/components/Content/Blocks/index.tsx +++ b/components/Content/Blocks/index.tsx @@ -3,6 +3,7 @@ import JsonToHtml from "@/components/JsonToHtml" import Shortcuts from "@/components/MyPages/Blocks/Shortcuts" import CardsGrid from "./CardsGrid" +import TextCols from "./TextCols" import type { BlocksProps } from "@/types/components/content/blocks" import { ContentBlocksTypenameEnum } from "@/types/components/content/enums" @@ -38,6 +39,8 @@ export function Blocks({ blocks }: BlocksProps) { firstItem={firstItem} /> ) + case ContentBlocksTypenameEnum.ContentPageBlocksTextCols: + return default: return null } diff --git a/components/LanguageSwitcher/index.tsx b/components/LanguageSwitcher/index.tsx index 7a5ecb6b2..3d0543051 100644 --- a/components/LanguageSwitcher/index.tsx +++ b/components/LanguageSwitcher/index.tsx @@ -1,6 +1,5 @@ "use client" -import { useEffect } from "react" import { useIntl } from "react-intl" import { languages } from "@/constants/languages" @@ -28,12 +27,16 @@ export default function LanguageSwitcher({ }: LanguageSwitcherProps) { const intl = useIntl() const currentLanguage = useLang() - const { - toggleDropdown, - isFooterLanguageSwitcherOpen, - isHeaderLanguageSwitcherOpen, - isHeaderLanguageSwitcherMobileOpen, - } = useDropdownStore() + const toggleDropdown = useDropdownStore((state) => state.toggleDropdown) + const isFooterLanguageSwitcherOpen = useDropdownStore( + (state) => state.isFooterLanguageSwitcherOpen + ) + const isHeaderLanguageSwitcherOpen = useDropdownStore( + (state) => state.isHeaderLanguageSwitcherOpen + ) + const isHeaderLanguageSwitcherMobileOpen = useDropdownStore( + (state) => state.isHeaderLanguageSwitcherMobileOpen + ) const isFooter = type === LanguageSwitcherTypesEnum.Footer const isHeader = !isFooter @@ -58,17 +61,14 @@ export default function LanguageSwitcher({ } }) - useEffect(() => { - if (isFooter && isFooterLanguageSwitcherOpen) { - document.body.style.overflow = "hidden" - } else { - document.body.style.overflow = "" - } + function handleClick() { + const scrollPosition = window.scrollY + toggleDropdown(dropdownType) - return () => { - document.body.style.overflow = "" - } - }, [isFooter, isFooterLanguageSwitcherOpen]) + requestAnimationFrame(() => { + window.scrollTo(0, scrollPosition) + }) + } const classNames = languageSwitcherVariants({ color, position }) @@ -82,7 +82,7 @@ export default function LanguageSwitcher({ ? "Close language menu" : "Open language menu", })} - onClick={() => toggleDropdown(dropdownType)} + onClick={handleClick} > {languages[currentLanguage]} diff --git a/lib/graphql/Query/ContentPage.graphql b/lib/graphql/Query/ContentPage.graphql index dec4b0d6f..e84612c0c 100644 --- a/lib/graphql/Query/ContentPage.graphql +++ b/lib/graphql/Query/ContentPage.graphql @@ -98,6 +98,28 @@ query GetContentPage($locale: String!, $uid: String!) { } } } + ... on ContentPageBlocksTextCols { + __typename + text_cols { + columns { + title + text { + json + embedded_itemsConnection { + edges { + node { + __typename + ...LoyaltyPageLink + ...ContentPageLink + ...HotelPageLink + } + } + totalCount + } + } + } + } + } } title header { diff --git a/server/routers/contentstack/contentPage/output.ts b/server/routers/contentstack/contentPage/output.ts index 1aa324041..21d7b1708 100644 --- a/server/routers/contentstack/contentPage/output.ts +++ b/server/routers/contentstack/contentPage/output.ts @@ -12,12 +12,8 @@ import { SidebarDynamicComponentEnum, SidebarTypenameEnum, } from "@/types/components/content/enums" -import { ImageVaultAsset } from "@/types/components/imageVault" -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" // Block schemas export const contentPageBlockTextContent = z.object({ @@ -135,11 +131,29 @@ export const contentPageCards = z.object({ }), }) +export const contentPageTextCols = z.object({ + __typename: z.literal(ContentBlocksTypenameEnum.ContentPageBlocksTextCols), + text_cols: z.object({ + columns: z.array( + z.object({ + title: z.string(), + text: z.object({ + json: z.any(), + embedded_itemsConnection: z.object({ + edges: z.array(z.any()), + totalCount: z.number(), + }), + }), + }) + ), + }), +}) const contentPageBlockItem = z.discriminatedUnion("__typename", [ contentPageBlockTextContent, contentPageCards, contentPageDynamicContent, contentPageShortcuts, + contentPageTextCols, ]) export const contentPageSidebarTextContent = z.object({ diff --git a/server/trpc.ts b/server/trpc.ts index 7a7a7f7ea..e3085f216 100644 --- a/server/trpc.ts +++ b/server/trpc.ts @@ -4,13 +4,13 @@ import { ZodError } from "zod" import { env } from "@/env/server" -import { type Context, createContext } from "./context" import { badRequestError, internalServerError, sessionExpiredError, unauthorizedError, } from "./errors/trpc" +import { type Context, createContext } from "./context" import { fetchServiceToken } from "./tokenManager" import { transformer } from "./transformer" diff --git a/stores/main-menu.ts b/stores/main-menu.ts index 29e039e99..4ccbcb290 100644 --- a/stores/main-menu.ts +++ b/stores/main-menu.ts @@ -85,6 +85,11 @@ const useDropdownStore = create((set, get) => ({ state.isMyPagesMenuOpen = false state.isHeaderLanguageSwitcherOpen = false state.isHeaderLanguageSwitcherMobileOpen = false + if (state.isFooterLanguageSwitcherOpen) { + document.body.classList.add("overflow-hidden") + } else { + document.body.classList.remove("overflow-hidden") + } break } }) diff --git a/types/components/content/blocks.ts b/types/components/content/blocks.ts index a97ac9780..a29912359 100644 --- a/types/components/content/blocks.ts +++ b/types/components/content/blocks.ts @@ -7,6 +7,7 @@ import { Block, CardsGrid, DynamicContent, + TextCols, } from "@/types/trpc/routers/contentstack/contentPage" export type BlocksProps = { @@ -17,6 +18,10 @@ export type CardsGridProps = Pick & { firstItem?: boolean } +export type TextColsProps = { + textCols: TextCols["text_cols"] +} + export type DynamicContentProps = { dynamicContent: DynamicContent["dynamic_content"] firstItem: boolean diff --git a/types/components/content/enums.ts b/types/components/content/enums.ts index 3bc20ac48..cafb608fa 100644 --- a/types/components/content/enums.ts +++ b/types/components/content/enums.ts @@ -6,6 +6,7 @@ export enum ContentBlocksTypenameEnum { ContentPageBlocksShortcuts = "ContentPageBlocksShortcuts", ContentPageBlocksCardsGrid = "ContentPageBlocksCardsGrid", ContentPageBlocksDynamicContent = "ContentPageBlocksDynamicContent", + ContentPageBlocksTextCols = "ContentPageBlocksTextCols", } export enum CardsGridEnum { diff --git a/types/trpc/routers/contentstack/contentPage.ts b/types/trpc/routers/contentstack/contentPage.ts index 4c3f27ce6..29eaf983e 100644 --- a/types/trpc/routers/contentstack/contentPage.ts +++ b/types/trpc/routers/contentstack/contentPage.ts @@ -9,6 +9,7 @@ import { contentPageShortcuts, contentPageSidebarDynamicContent, contentPageSidebarTextContent, + contentPageTextCols, loyaltyCardBlock, validateContentPageRefsSchema, validateContentPageSchema, @@ -81,4 +82,22 @@ export type CardsGrid = Omit & { } export type CardsRaw = CardsGrid["cards_grid"]["cards"][number] -export type Block = RteBlockContent | Shortcuts | CardsGrid | DynamicContent +type TextColsRaw = z.infer +export interface TextCols extends TextColsRaw { + textCols: { + columns: { + title: string + text: { + json: RTEDocument + embedded_itemsConnection: EdgesWithTotalCount + } + }[] + } +} + +export type Block = + | RteBlockContent + | Shortcuts + | CardsGrid + | DynamicContent + | TextCols