Merge branch 'develop' into feat/feature-flags
This commit is contained in:
@@ -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;
|
||||
|
||||
29
components/Content/Blocks/TextCols/index.tsx
Normal file
29
components/Content/Blocks/TextCols/index.tsx
Normal file
@@ -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 (
|
||||
<div className={styles.columns}>
|
||||
{textCols.columns.map((col) => {
|
||||
return (
|
||||
<section key={col.title} className={styles.column}>
|
||||
<Subtitle>{col.title}</Subtitle>
|
||||
<div className={styles.text}>
|
||||
<JsonToHtml
|
||||
nodes={col.text.json.children}
|
||||
embeds={col.text.embedded_itemsConnection.edges}
|
||||
renderOptions={renderOptions}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
70
components/Content/Blocks/TextCols/renderOptions.tsx
Normal file
70
components/Content/Blocks/TextCols/renderOptions.tsx
Normal file
@@ -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 (
|
||||
<p key={node.uid} className={styles.p}>
|
||||
{next(node.children, embeds, fullRenderOptions)}
|
||||
</p>
|
||||
)
|
||||
},
|
||||
[RTETypeEnum.a]: (
|
||||
node: RTERegularNode,
|
||||
embeds: EmbedByUid,
|
||||
next: RTENext,
|
||||
fullRenderOptions: RenderOptions
|
||||
) => {
|
||||
if (node.attrs.url) {
|
||||
return (
|
||||
<a
|
||||
href={node.attrs.url}
|
||||
target={node.attrs.target ?? "_blank"}
|
||||
key={node.uid}
|
||||
className={styles.a}
|
||||
>
|
||||
{next(node.children, embeds, fullRenderOptions)}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
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 (
|
||||
<Link href={href} key={node.uid} className={styles.a}>
|
||||
{next(node.children, embeds, fullRenderOptions)}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
},
|
||||
}
|
||||
40
components/Content/Blocks/TextCols/textcols.module.css
Normal file
40
components/Content/Blocks/TextCols/textcols.module.css
Normal file
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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 <TextCols textCols={block.text_cols} />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
>
|
||||
<GlobeIcon width={20} height={20} color={color} />
|
||||
<span>{languages[currentLanguage]}</span>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -85,6 +85,11 @@ const useDropdownStore = create<DropdownState>((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
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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<CardsGrid, "cards_grid"> & {
|
||||
firstItem?: boolean
|
||||
}
|
||||
|
||||
export type TextColsProps = {
|
||||
textCols: TextCols["text_cols"]
|
||||
}
|
||||
|
||||
export type DynamicContentProps = {
|
||||
dynamicContent: DynamicContent["dynamic_content"]
|
||||
firstItem: boolean
|
||||
|
||||
@@ -6,6 +6,7 @@ export enum ContentBlocksTypenameEnum {
|
||||
ContentPageBlocksShortcuts = "ContentPageBlocksShortcuts",
|
||||
ContentPageBlocksCardsGrid = "ContentPageBlocksCardsGrid",
|
||||
ContentPageBlocksDynamicContent = "ContentPageBlocksDynamicContent",
|
||||
ContentPageBlocksTextCols = "ContentPageBlocksTextCols",
|
||||
}
|
||||
|
||||
export enum CardsGridEnum {
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
contentPageShortcuts,
|
||||
contentPageSidebarDynamicContent,
|
||||
contentPageSidebarTextContent,
|
||||
contentPageTextCols,
|
||||
loyaltyCardBlock,
|
||||
validateContentPageRefsSchema,
|
||||
validateContentPageSchema,
|
||||
@@ -81,4 +82,22 @@ export type CardsGrid = Omit<CardsGridRaw, "cards"> & {
|
||||
}
|
||||
export type CardsRaw = CardsGrid["cards_grid"]["cards"][number]
|
||||
|
||||
export type Block = RteBlockContent | Shortcuts | CardsGrid | DynamicContent
|
||||
type TextColsRaw = z.infer<typeof contentPageTextCols>
|
||||
export interface TextCols extends TextColsRaw {
|
||||
textCols: {
|
||||
columns: {
|
||||
title: string
|
||||
text: {
|
||||
json: RTEDocument
|
||||
embedded_itemsConnection: EdgesWithTotalCount<Embeds>
|
||||
}
|
||||
}[]
|
||||
}
|
||||
}
|
||||
|
||||
export type Block =
|
||||
| RteBlockContent
|
||||
| Shortcuts
|
||||
| CardsGrid
|
||||
| DynamicContent
|
||||
| TextCols
|
||||
|
||||
Reference in New Issue
Block a user