Merge branch 'develop' into feat/sw-386-header-fixes
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import SectionContainer from "@/components/Section/Container"
|
||||
import SectionHeader from "@/components/Section/Header"
|
||||
import Card from "@/components/TempDesignSystem/Card"
|
||||
import ContentCard from "@/components/TempDesignSystem/ContentCard"
|
||||
import Grids from "@/components/TempDesignSystem/Grids"
|
||||
import LoyaltyCard from "@/components/TempDesignSystem/LoyaltyCard"
|
||||
|
||||
@@ -21,8 +22,19 @@ export default function CardsGrid({
|
||||
<Grids.Stackable>
|
||||
{cards_grid.cards.map((card) => {
|
||||
switch (card.__typename) {
|
||||
case CardsGridEnum.Card: {
|
||||
return (
|
||||
case CardsGridEnum.Card:
|
||||
return card.isContentCard ? (
|
||||
<ContentCard
|
||||
key={card.system.uid}
|
||||
title={card.heading || ""}
|
||||
description={card.body_text || ""}
|
||||
primaryButton={card.primaryButton}
|
||||
secondaryButton={card.secondaryButton}
|
||||
sidePeekButton={card.sidePeekButton}
|
||||
backgroundImage={card.background_image}
|
||||
style="default"
|
||||
/>
|
||||
) : (
|
||||
<Card
|
||||
theme={cards_grid.theme || "one"}
|
||||
key={card.system.uid}
|
||||
@@ -33,7 +45,6 @@ export default function CardsGrid({
|
||||
primaryButton={card.primaryButton}
|
||||
/>
|
||||
)
|
||||
}
|
||||
case CardsGridEnum.LoyaltyCard:
|
||||
return (
|
||||
<LoyaltyCard
|
||||
|
||||
@@ -74,7 +74,7 @@ export function RoomCard({
|
||||
onClick={handleRoomCtaClick}
|
||||
>
|
||||
{intl.formatMessage({ id: "See room details" })}
|
||||
<ChevronRightIcon className={styles.chevron} />
|
||||
<ChevronRightIcon />
|
||||
</Button>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import useDropdownStore from "@/stores/main-menu"
|
||||
|
||||
import { ChevronLeftIcon } from "@/components/Icons"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
|
||||
import styles from "./languageSwitcherContainer.module.css"
|
||||
|
||||
import { DropdownTypeEnum } from "@/types/components/dropdown/dropdown"
|
||||
import {
|
||||
type LanguageSwitcherContainerProps,
|
||||
LanguageSwitcherTypesEnum,
|
||||
} from "@/types/components/languageSwitcher/languageSwitcher"
|
||||
|
||||
export default function LanguageSwitcherContainer({
|
||||
children,
|
||||
type,
|
||||
}: LanguageSwitcherContainerProps) {
|
||||
const { toggleDropdown } = useDropdownStore()
|
||||
const intl = useIntl()
|
||||
const isFooter = type === LanguageSwitcherTypesEnum.Footer
|
||||
const isMobileHeader = type === LanguageSwitcherTypesEnum.MobileHeader
|
||||
const position = isFooter
|
||||
? DropdownTypeEnum.FooterLanguageSwitcher
|
||||
: DropdownTypeEnum.HamburgerMenu
|
||||
|
||||
return (
|
||||
<div>
|
||||
{isMobileHeader ? (
|
||||
<div className={styles.backWrapper}>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.backButton}
|
||||
onClick={() => toggleDropdown(position)}
|
||||
>
|
||||
<ChevronLeftIcon color="red" />
|
||||
<Subtitle type="one">
|
||||
{intl.formatMessage({
|
||||
id: "Main menu",
|
||||
})}
|
||||
</Subtitle>
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
{isFooter ? (
|
||||
<div className={styles.closeWrapper}>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.closeButton}
|
||||
aria-label={intl.formatMessage({
|
||||
id: "Close menu",
|
||||
})}
|
||||
onClick={() => toggleDropdown(position)}
|
||||
>
|
||||
<span className={styles.bar}></span>
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
.backWrapper {
|
||||
background-color: var(--Base-Surface-Secondary-light-Normal);
|
||||
padding: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.backButton {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.closeWrapper {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: var(--Spacing-x2);
|
||||
border-bottom: 1px solid var(--Base-Border-Subtle);
|
||||
}
|
||||
|
||||
.closeButton {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
justify-self: flex-start;
|
||||
padding: 11px var(--Spacing-x1) var(--Spacing-x2);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.bar,
|
||||
.bar::after,
|
||||
.bar::before {
|
||||
background: var(--Base-Text-High-contrast);
|
||||
border-radius: 2.3px;
|
||||
display: inline-block;
|
||||
height: 3px;
|
||||
position: relative;
|
||||
transition: all 0.3s;
|
||||
width: var(--Spacing-x4);
|
||||
}
|
||||
|
||||
.bar::after,
|
||||
.bar::before {
|
||||
content: "";
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
transform-origin: 50% 50%;
|
||||
width: var(--Spacing-x4);
|
||||
}
|
||||
|
||||
.bar {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.bar::after {
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.bar::before {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.closeWrapper {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,8 @@
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { Lang, languages } from "@/constants/languages"
|
||||
import useDropdownStore from "@/stores/main-menu"
|
||||
|
||||
import { CheckIcon, ChevronLeftIcon } from "@/components/Icons"
|
||||
import { CheckIcon } from "@/components/Icons"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import useLang from "@/hooks/useLang"
|
||||
@@ -13,38 +12,19 @@ import { useTrapFocus } from "@/hooks/useTrapFocus"
|
||||
|
||||
import styles from "./languageSwitcherContent.module.css"
|
||||
|
||||
import { DropdownTypeEnum } from "@/types/components/dropdown/dropdown"
|
||||
import type { LanguageSwitcherProps } from "@/types/components/languageSwitcher/languageSwitcher"
|
||||
import type { LanguageSwitcherContentProps } from "@/types/components/languageSwitcher/languageSwitcher"
|
||||
|
||||
export default function LanguageSwitcherContent({
|
||||
urls,
|
||||
type,
|
||||
}: LanguageSwitcherProps) {
|
||||
}: LanguageSwitcherContentProps) {
|
||||
const intl = useIntl()
|
||||
const currentLanguage = useLang()
|
||||
const { toggleDropdown } = useDropdownStore()
|
||||
|
||||
const languageSwitcherRef = useTrapFocus()
|
||||
const urlKeys = Object.keys(urls) as Lang[]
|
||||
const position =
|
||||
type === "footer"
|
||||
? DropdownTypeEnum.FooterLanguageSwitcher
|
||||
: DropdownTypeEnum.HamburgerMenu
|
||||
|
||||
return (
|
||||
<div className={styles.languageSwitcherContent} ref={languageSwitcherRef}>
|
||||
{type === "mobileHeader" ? (
|
||||
<div className={styles.backWrapper}>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.backButton}
|
||||
onClick={() => toggleDropdown(position)}
|
||||
>
|
||||
<ChevronLeftIcon color="red" />
|
||||
<Subtitle type="one">Main Menu</Subtitle>
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className={styles.languageWrapper}>
|
||||
<Subtitle className={styles.subtitle} type="two">
|
||||
{intl.formatMessage({ id: "Select your language" })}
|
||||
|
||||
@@ -1,22 +1,3 @@
|
||||
.backWrapper {
|
||||
background-color: var(--Base-Surface-Secondary-light-Normal);
|
||||
padding: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.backButton {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: var(--Base-Text-High-contrast);
|
||||
font-family: var(--typography-Subtitle-1-fontFamily);
|
||||
font-weight: var(--typography-Subtitle-1-fontWeight);
|
||||
font-size: var(--typography-Subtitle-1-Mobile-fontSize);
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.languageWrapper {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x3);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { useEffect } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { languages } from "@/constants/languages"
|
||||
@@ -9,13 +10,17 @@ import { ChevronDownIcon, GlobeIcon } from "@/components/Icons"
|
||||
import { useHandleKeyUp } from "@/hooks/useHandleKeyUp"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
import LanguageSwitcherContainer from "./LanguageSwitcherContainer"
|
||||
import LanguageSwitcherContent from "./LanguageSwitcherContent"
|
||||
import { languageSwitcherVariants } from "./variants"
|
||||
|
||||
import styles from "./languageSwitcher.module.css"
|
||||
|
||||
import { DropdownTypeEnum } from "@/types/components/dropdown/dropdown"
|
||||
import type { LanguageSwitcherProps } from "@/types/components/languageSwitcher/languageSwitcher"
|
||||
import {
|
||||
type LanguageSwitcherProps,
|
||||
LanguageSwitcherTypesEnum,
|
||||
} from "@/types/components/languageSwitcher/languageSwitcher"
|
||||
|
||||
export default function LanguageSwitcher({
|
||||
urls,
|
||||
@@ -30,8 +35,11 @@ export default function LanguageSwitcher({
|
||||
isHeaderLanguageSwitcherMobileOpen,
|
||||
} = useDropdownStore()
|
||||
|
||||
const position = type === "footer" ? "footer" : "header"
|
||||
const color = type === "footer" ? "pale" : "burgundy"
|
||||
const isFooter = type === LanguageSwitcherTypesEnum.Footer
|
||||
const isHeader = !isFooter
|
||||
|
||||
const position = isFooter ? "footer" : "header"
|
||||
const color = isFooter ? "pale" : "burgundy"
|
||||
|
||||
const dropdownType = {
|
||||
footer: DropdownTypeEnum.FooterLanguageSwitcher,
|
||||
@@ -40,8 +48,8 @@ export default function LanguageSwitcher({
|
||||
}[type]
|
||||
|
||||
const isLanguageSwitcherOpen =
|
||||
(type === "footer" && isFooterLanguageSwitcherOpen) ||
|
||||
(type !== "footer" &&
|
||||
(isFooter && isFooterLanguageSwitcherOpen) ||
|
||||
(isHeader &&
|
||||
(isHeaderLanguageSwitcherOpen || isHeaderLanguageSwitcherMobileOpen))
|
||||
|
||||
useHandleKeyUp((event: KeyboardEvent) => {
|
||||
@@ -50,6 +58,18 @@ export default function LanguageSwitcher({
|
||||
}
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (isFooter && isFooterLanguageSwitcherOpen) {
|
||||
document.body.style.overflow = "hidden"
|
||||
} else {
|
||||
document.body.style.overflow = ""
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.body.style.overflow = ""
|
||||
}
|
||||
}, [isFooter, isFooterLanguageSwitcherOpen])
|
||||
|
||||
const classNames = languageSwitcherVariants({ color, position })
|
||||
|
||||
return (
|
||||
@@ -78,7 +98,9 @@ export default function LanguageSwitcher({
|
||||
className={`${styles.dropdown} ${isLanguageSwitcherOpen ? styles.isExpanded : ""}`}
|
||||
>
|
||||
{isLanguageSwitcherOpen ? (
|
||||
<LanguageSwitcherContent urls={urls} type={type} />
|
||||
<LanguageSwitcherContainer type={type}>
|
||||
<LanguageSwitcherContent urls={urls} />
|
||||
</LanguageSwitcherContainer>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -31,20 +31,36 @@
|
||||
|
||||
.dropdown {
|
||||
position: fixed;
|
||||
top: var(--main-menu-mobile-height);
|
||||
right: -100vw;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
transition: right 0.3s;
|
||||
z-index: var(--menu-overlay-z-index);
|
||||
}
|
||||
|
||||
.dropdown.isExpanded {
|
||||
.top .dropdown {
|
||||
right: -100vw;
|
||||
top: var(--main-menu-mobile-height);
|
||||
bottom: 0;
|
||||
transition: right 0.3s;
|
||||
}
|
||||
|
||||
.top .dropdown.isExpanded {
|
||||
display: block;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.bottom .dropdown {
|
||||
transition: transform 0.3s;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
.bottom .dropdown.isExpanded {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.languageSwitcher {
|
||||
position: relative;
|
||||
@@ -81,10 +97,16 @@
|
||||
}
|
||||
|
||||
.bottom .dropdown {
|
||||
top: auto;
|
||||
transition: none;
|
||||
height: auto;
|
||||
left: -100%;
|
||||
bottom: 2.25rem;
|
||||
}
|
||||
|
||||
.bottom .dropdown.isExpanded {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.bottom .dropdown::before {
|
||||
top: 100%;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
.card {
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 399px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.default {
|
||||
background-color: var(--Base-Surface-Subtle-Normal);
|
||||
}
|
||||
|
||||
.featured {
|
||||
background-color: var(--Main-Grey-White);
|
||||
}
|
||||
|
||||
.default,
|
||||
.featured {
|
||||
border: 1px solid var(--Base-Border-Subtle);
|
||||
}
|
||||
|
||||
.imageContainer {
|
||||
width: 100%;
|
||||
height: 12.58625rem; /* 201.38px / 16 = 12.58625rem */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.backgroundImage {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x-one-and-half);
|
||||
align-items: flex-start;
|
||||
padding: var(--Spacing-x2) var(--Spacing-x3);
|
||||
}
|
||||
|
||||
.description {
|
||||
color: var(--Base-Text-Medium-contrast);
|
||||
}
|
||||
|
||||
.ctaContainer {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--Spacing-x1);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ctaButton {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: 1367px) {
|
||||
.card:not(.alwaysStack) .ctaContainer {
|
||||
grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.card:not(.alwaysStack) .ctaContainer:has(:only-child) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.sidePeekCTA {
|
||||
/* TODO: Create ticket to remove padding on "link" buttons,
|
||||
align w. design on this. */
|
||||
padding: 0 !important;
|
||||
}
|
||||
98
components/TempDesignSystem/ContentCard/index.tsx
Normal file
98
components/TempDesignSystem/ContentCard/index.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
import React from "react"
|
||||
|
||||
import { ChevronRightIcon } from "@/components/Icons"
|
||||
import Image from "@/components/Image"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
|
||||
import Subtitle from "../Text/Subtitle"
|
||||
import { contentCardVariants } from "./variants"
|
||||
|
||||
import styles from "./contentCard.module.css"
|
||||
|
||||
import type { ContentCardProps } from "@/types/components/contentCard"
|
||||
|
||||
export default function ContentCard({
|
||||
title,
|
||||
description,
|
||||
primaryButton,
|
||||
secondaryButton,
|
||||
sidePeekButton,
|
||||
backgroundImage,
|
||||
style = "default",
|
||||
alwaysStack = false,
|
||||
className,
|
||||
}: ContentCardProps) {
|
||||
const cardClasses = contentCardVariants({ style, alwaysStack, className })
|
||||
|
||||
return (
|
||||
<div className={cardClasses}>
|
||||
{backgroundImage && (
|
||||
<div className={styles.imageContainer}>
|
||||
<Image
|
||||
src={backgroundImage.url}
|
||||
alt={backgroundImage.meta?.alt || ""}
|
||||
className={styles.backgroundImage}
|
||||
width={399}
|
||||
height={201}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.content}>
|
||||
<Subtitle textAlign="left" type="two" color="black">
|
||||
{title}
|
||||
</Subtitle>
|
||||
<Body color="black">{description}</Body>
|
||||
{!!sidePeekButton ? (
|
||||
<Button
|
||||
// onClick={() => {
|
||||
// // TODO: Implement sidePeek functionality once SW-341 is merged.
|
||||
// }}
|
||||
theme="base"
|
||||
variant="icon"
|
||||
intent="text"
|
||||
size="small"
|
||||
className={styles.sidePeekCTA}
|
||||
>
|
||||
{sidePeekButton.title}
|
||||
<ChevronRightIcon />
|
||||
</Button>
|
||||
) : (
|
||||
<div className={styles.ctaContainer}>
|
||||
{primaryButton && (
|
||||
<Button
|
||||
asChild
|
||||
intent="primary"
|
||||
size="small"
|
||||
className={styles.ctaButton}
|
||||
>
|
||||
<Link
|
||||
href={primaryButton.href}
|
||||
target={primaryButton.openInNewTab ? "_blank" : undefined}
|
||||
>
|
||||
{primaryButton.title}
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
{secondaryButton && (
|
||||
<Button
|
||||
asChild
|
||||
intent="secondary"
|
||||
size="small"
|
||||
className={styles.ctaButton}
|
||||
>
|
||||
<Link
|
||||
href={secondaryButton.href}
|
||||
target={secondaryButton.openInNewTab ? "_blank" : undefined}
|
||||
>
|
||||
{secondaryButton.title}
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
20
components/TempDesignSystem/ContentCard/variants.ts
Normal file
20
components/TempDesignSystem/ContentCard/variants.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { cva } from "class-variance-authority"
|
||||
|
||||
import styles from "./contentCard.module.css"
|
||||
|
||||
export const contentCardVariants = cva(styles.card, {
|
||||
variants: {
|
||||
style: {
|
||||
default: styles.default,
|
||||
featured: styles.featured,
|
||||
},
|
||||
alwaysStack: {
|
||||
true: styles.alwaysStack,
|
||||
false: "",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
style: "default",
|
||||
alwaysStack: false,
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user