Merged in feat/sw-187-footer-content-stack (pull request #565)
Feat/sw 187 footer content stack Approved-by: Erik Tiekstra Approved-by: Matilda Landström
This commit is contained in:
@@ -8,7 +8,7 @@ import Navigation from "./Navigation"
|
||||
import styles from "./footer.module.css"
|
||||
|
||||
export default async function Footer() {
|
||||
const footerData = await serverClient().contentstack.base.footer({
|
||||
const footerData = await serverClient().contentstack.base.currentFooter({
|
||||
lang: getLang(),
|
||||
})
|
||||
if (!footerData) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import LoginButton from "../LoginButton"
|
||||
import styles from "./mainMenu.module.css"
|
||||
|
||||
import type { MainMenuProps } from "@/types/components/current/header/mainMenu"
|
||||
import { DropdownTypeEnum } from "@/types/components/dropdown/dropdown"
|
||||
|
||||
export function MainMenu({
|
||||
frontpageLinkText,
|
||||
@@ -61,12 +62,8 @@ export function MainMenu({
|
||||
"/sv/current-content-page",
|
||||
].includes(pathname)
|
||||
|
||||
const {
|
||||
isHamburgerMenuOpen,
|
||||
isMyPagesMobileMenuOpen,
|
||||
toggleHamburgerMenu,
|
||||
toggleMyPagesMobileMenu,
|
||||
} = useDropdownStore()
|
||||
const { toggleDropdown, isMyPagesMobileMenuOpen, isHamburgerMenuOpen } =
|
||||
useDropdownStore()
|
||||
|
||||
function handleMyPagesMobileMenuClick() {
|
||||
// Only track click when opening it
|
||||
@@ -74,7 +71,7 @@ export function MainMenu({
|
||||
trackClick("profile picture icon")
|
||||
}
|
||||
|
||||
toggleMyPagesMobileMenu()
|
||||
toggleDropdown(DropdownTypeEnum.MyPagesMobileMenu)
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -89,7 +86,7 @@ export function MainMenu({
|
||||
<button
|
||||
aria-pressed="false"
|
||||
className={`${styles.expanderBtn} ${isHamburgerMenuOpen ? styles.expanded : ""}`}
|
||||
onClick={toggleHamburgerMenu}
|
||||
onClick={() => toggleDropdown(DropdownTypeEnum.HamburgerMenu)}
|
||||
type="button"
|
||||
>
|
||||
<span className={styles.iconBars}></span>
|
||||
|
||||
@@ -13,6 +13,8 @@ import useLang from "@/hooks/useLang"
|
||||
|
||||
import styles from "./my-pages-mobile-dropdown.module.css"
|
||||
|
||||
import { DropdownTypeEnum } from "@/types/components/dropdown/dropdown"
|
||||
|
||||
type Navigation = Awaited<ReturnType<(typeof navigationQueryRouter)["get"]>>
|
||||
|
||||
export default function MyPagesMobileDropdown({
|
||||
@@ -22,8 +24,7 @@ export default function MyPagesMobileDropdown({
|
||||
}) {
|
||||
const { formatMessage } = useIntl()
|
||||
const lang = useLang()
|
||||
const { toggleMyPagesMobileMenu, isMyPagesMobileMenuOpen } =
|
||||
useDropdownStore()
|
||||
const { toggleDropdown, isMyPagesMobileMenuOpen } = useDropdownStore()
|
||||
|
||||
if (!navigation) {
|
||||
return null
|
||||
@@ -51,7 +52,9 @@ export default function MyPagesMobileDropdown({
|
||||
size={menuItem.display_sign_out_link ? "small" : "regular"}
|
||||
variant="myPageMobileDropdown"
|
||||
color="burgundy"
|
||||
onClick={toggleMyPagesMobileMenu}
|
||||
onClick={() =>
|
||||
toggleDropdown(DropdownTypeEnum.MyPagesMobileMenu)
|
||||
}
|
||||
>
|
||||
{link.linkText}
|
||||
</Link>
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1367px) {
|
||||
@media screen and (min-width: 767px) {
|
||||
.details {
|
||||
padding: var(--Spacing-x6) var(--Spacing-x5) var(--Spacing-x4);
|
||||
}
|
||||
@@ -65,5 +65,6 @@
|
||||
border-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
gap: var(--Spacing-x4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher"
|
||||
import { getIconByIconName } from "@/components/Icons/get-icon-by-icon-name"
|
||||
import Image from "@/components/Image"
|
||||
import LanguageSwitcher from "@/components/LanguageSwitcher"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
|
||||
import { footer } from "../mockedData"
|
||||
|
||||
import styles from "./details.module.css"
|
||||
|
||||
import type { FooterDetailsProps } from "@/types/components/footer/navigation"
|
||||
import type { SocialIconsProps } from "@/types/components/footer/socialIcons"
|
||||
import { IconName } from "@/types/components/icon"
|
||||
|
||||
@@ -17,16 +17,15 @@ function SocialIcon({ iconName }: SocialIconsProps) {
|
||||
return SocialIcon ? <SocialIcon color="white" /> : <span>{iconName}</span>
|
||||
}
|
||||
|
||||
export default function FooterDetails() {
|
||||
export default async function FooterDetails({
|
||||
socialMedia,
|
||||
tertiaryLinks,
|
||||
languageUrls,
|
||||
}: FooterDetailsProps) {
|
||||
const lang = getLang()
|
||||
const { formatMessage } = await getIntl()
|
||||
const currentYear = new Date().getFullYear()
|
||||
const {
|
||||
socialMedia,
|
||||
copyrightCompany,
|
||||
copyrightInfo,
|
||||
tertiaryLinks,
|
||||
languageSwitcher,
|
||||
} = footer
|
||||
|
||||
return (
|
||||
<section className={styles.details}>
|
||||
<div className={styles.topContainer}>
|
||||
@@ -43,48 +42,51 @@ export default function FooterDetails() {
|
||||
/>
|
||||
</Link>
|
||||
<nav className={styles.socialNav}>
|
||||
{socialMedia.links.map((link) => (
|
||||
<a
|
||||
className={styles.socialLink}
|
||||
color="white"
|
||||
href={link.href}
|
||||
key={link.id}
|
||||
target="_blank"
|
||||
aria-label={link.title}
|
||||
>
|
||||
<SocialIcon iconName={link.title} />
|
||||
</a>
|
||||
))}
|
||||
{socialMedia?.links.map(
|
||||
(link) =>
|
||||
link.href && (
|
||||
<a
|
||||
className={styles.socialLink}
|
||||
color="white"
|
||||
href={link.href.href}
|
||||
key={link.href.title}
|
||||
target="_blank"
|
||||
aria-label={link.href.title}
|
||||
>
|
||||
<SocialIcon iconName={link.href.title} />
|
||||
</a>
|
||||
)
|
||||
)}
|
||||
</nav>
|
||||
</div>
|
||||
<div className={styles.bottomContainer}>
|
||||
<div className={styles.copyrightContainer}>
|
||||
<Footnote textTransform="uppercase">
|
||||
© {currentYear} {copyrightCompany}
|
||||
</Footnote>
|
||||
<Footnote textTransform="uppercase" color="peach50">
|
||||
{copyrightInfo}
|
||||
© {currentYear}{" "}
|
||||
{formatMessage({ id: "Copyright all rights reserved" })}
|
||||
</Footnote>
|
||||
</div>
|
||||
<div className={styles.navigationContainer}>
|
||||
<nav className={styles.navigation}>
|
||||
{tertiaryLinks.map((link) => (
|
||||
<Footnote asChild textTransform="uppercase" key={link.id}>
|
||||
<Link
|
||||
className={styles.link}
|
||||
color="peach50"
|
||||
href={link.href}
|
||||
target="_blank"
|
||||
>
|
||||
{link.title}
|
||||
</Link>
|
||||
</Footnote>
|
||||
))}
|
||||
{tertiaryLinks?.map(
|
||||
(link) =>
|
||||
link.url && (
|
||||
<Footnote asChild textTransform="uppercase" key={link.title}>
|
||||
<Link
|
||||
className={styles.link}
|
||||
color="peach50"
|
||||
href={link.url}
|
||||
target="_blank"
|
||||
>
|
||||
{link.title}
|
||||
</Link>
|
||||
</Footnote>
|
||||
)
|
||||
)}
|
||||
</nav>
|
||||
{
|
||||
// This will be changed to the new LangueSwitcher that is done in the header branch, when implementing contentstack
|
||||
}
|
||||
<LanguageSwitcher urls={languageSwitcher.urls} />
|
||||
{languageUrls && (
|
||||
<LanguageSwitcher type="footer" urls={languageUrls} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -11,11 +11,11 @@ export default function FooterMainNav({ mainLinks }: FooterMainNavProps) {
|
||||
<nav className={styles.mainNavigation}>
|
||||
<ul className={styles.mainNavigationList}>
|
||||
{mainLinks.map((link) => (
|
||||
<li key={link.id} className={styles.mainNavigationItem}>
|
||||
<li key={link.title} className={styles.mainNavigationItem}>
|
||||
<Subtitle type="two" asChild>
|
||||
<Link
|
||||
color="burgundy"
|
||||
href={link.href}
|
||||
href={link.url}
|
||||
className={styles.mainNavigationLink}
|
||||
>
|
||||
{link.title}
|
||||
|
||||
@@ -1,60 +1,78 @@
|
||||
import Image from "@/components/Image"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
|
||||
import styles from "./secondarynav.module.css"
|
||||
|
||||
import {
|
||||
AppDownLoadLinks,
|
||||
type FooterSecondaryNavProps,
|
||||
} from "@/types/components/footer/navigation"
|
||||
import { AppDownLoadLinks } from "@/types/components/footer/appDownloadIcons"
|
||||
import { type FooterSecondaryNavProps } from "@/types/components/footer/navigation"
|
||||
|
||||
export default function FooterSecondaryNav({
|
||||
secondaryLinks,
|
||||
appDownloads,
|
||||
}: FooterSecondaryNavProps) {
|
||||
const lang = getLang()
|
||||
|
||||
return (
|
||||
<div className={styles.secondaryNavigation}>
|
||||
<nav className={styles.secondaryNavigationGroup}>
|
||||
<Body color="peach80" textTransform="uppercase">
|
||||
{appDownloads.title}
|
||||
</Body>
|
||||
<ul className={styles.secondaryNavigationList}>
|
||||
{appDownloads.links.map((link) => (
|
||||
<li key={link.id} className={styles.appDownloadItem}>
|
||||
<a href={link.href} target="_blank" aria-label={link.title}>
|
||||
<Image
|
||||
src={
|
||||
AppDownLoadLinks[link.id as keyof typeof AppDownLoadLinks]
|
||||
}
|
||||
alt={link.title}
|
||||
width={125}
|
||||
height={40}
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
{appDownloads && (
|
||||
<nav className={styles.secondaryNavigationGroup}>
|
||||
<Body color="peach80" textTransform="uppercase">
|
||||
{appDownloads.title}
|
||||
</Body>
|
||||
<ul className={styles.secondaryNavigationList}>
|
||||
{appDownloads.links.map(
|
||||
(link) =>
|
||||
link.href && (
|
||||
<li key={link.type} className={styles.appDownloadItem}>
|
||||
<a
|
||||
href={link.href.href}
|
||||
target="_blank"
|
||||
aria-label={link.href.title}
|
||||
>
|
||||
<Image
|
||||
src={
|
||||
AppDownLoadLinks[
|
||||
`${link.type}_${lang}` as keyof typeof AppDownLoadLinks
|
||||
]
|
||||
}
|
||||
alt=""
|
||||
width={125}
|
||||
height={40}
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
)}
|
||||
</ul>
|
||||
</nav>
|
||||
)}
|
||||
{secondaryLinks.map((link) => (
|
||||
<nav className={styles.secondaryNavigationGroup} key={link.title}>
|
||||
<Body color="peach80" textTransform="uppercase">
|
||||
{link.title}
|
||||
</Body>
|
||||
<ul className={styles.secondaryNavigationList}>
|
||||
{link.links.map((link) => (
|
||||
<li key={link.id} className={styles.secondaryNavigationItem}>
|
||||
{link?.links?.map((link) => (
|
||||
<li key={link.title} className={styles.secondaryNavigationItem}>
|
||||
{link.isExternal ? (
|
||||
<a
|
||||
href={link.href}
|
||||
href={link.url}
|
||||
key={link.title}
|
||||
target={link.openInNewTab ? "_blank" : "_self"}
|
||||
aria-label={link.title}
|
||||
className={styles.secondaryNavigationLink}
|
||||
>
|
||||
{link.title}
|
||||
</a>
|
||||
) : (
|
||||
<Link href={link.href} key={link.title}>
|
||||
<Link
|
||||
href={link.url}
|
||||
key={link.title}
|
||||
target={link.openInNewTab ? "_blank" : "_self"}
|
||||
color="burgundy"
|
||||
>
|
||||
{link.title}
|
||||
</Link>
|
||||
)}
|
||||
|
||||
@@ -25,10 +25,24 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.secondaryNavigationLink {
|
||||
color: var(--Base-Text-High-contrast);
|
||||
font-weight: var(--typography-Body-Bold-fontWeight);
|
||||
font-family: var(--typography-Body-Bold-fontFamily);
|
||||
margin: 0;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 767px) {
|
||||
.secondaryNavigation {
|
||||
margin-top: var(--Spacing-x4);
|
||||
gap: 120px;
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1367px) {
|
||||
.secondaryNavigation {
|
||||
margin-top: 0;
|
||||
gap: 80px;
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import { footer } from "../mockedData"
|
||||
import FooterMainNav from "./MainNav"
|
||||
import FooterSecondaryNav from "./SecondaryNav"
|
||||
|
||||
import styles from "./navigation.module.css"
|
||||
|
||||
export default function FooterNavigation() {
|
||||
const { mainLinks, secondaryLinks, appDownloads } = footer
|
||||
import type { FooterNavigationProps } from "@/types/components/footer/navigation"
|
||||
|
||||
export default function FooterNavigation({
|
||||
mainLinks,
|
||||
secondaryLinks,
|
||||
appDownloads,
|
||||
}: FooterNavigationProps) {
|
||||
return (
|
||||
<section className={styles.section}>
|
||||
<div className={styles.maxWidth}>
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
max-width: var(--max-width-content);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 767px) {
|
||||
.section {
|
||||
padding: var(--Spacing-x9) var(--Spacing-x5);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1367px) {
|
||||
.section {
|
||||
padding: var(--Spacing-x9) 0;
|
||||
|
||||
@@ -1,11 +1,27 @@
|
||||
import { serverClient } from "@/lib/trpc/server"
|
||||
|
||||
import FooterDetails from "./Details"
|
||||
import FooterNavigation from "./Navigation"
|
||||
|
||||
export default function Footer() {
|
||||
export default async function Footer() {
|
||||
const footerData = await serverClient().contentstack.base.footer()
|
||||
const languages = await serverClient().contentstack.languageSwitcher.get()
|
||||
|
||||
if (!footerData || !languages) {
|
||||
return <FooterDetails />
|
||||
}
|
||||
return (
|
||||
<footer>
|
||||
<FooterNavigation />
|
||||
<FooterDetails />
|
||||
<FooterNavigation
|
||||
mainLinks={footerData.mainLinks}
|
||||
secondaryLinks={footerData.secondaryLinks}
|
||||
appDownloads={footerData.appDownloads}
|
||||
/>
|
||||
<FooterDetails
|
||||
socialMedia={footerData.socialMedia}
|
||||
tertiaryLinks={footerData.tertiaryLinks}
|
||||
languageUrls={languages.urls}
|
||||
/>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,192 +0,0 @@
|
||||
export const footer = {
|
||||
mainLinks: [
|
||||
{
|
||||
title: "Travel guides",
|
||||
href: "/travel-guides",
|
||||
id: "travel-guides",
|
||||
openInNewTab: false,
|
||||
isExternal: false,
|
||||
},
|
||||
{
|
||||
title: "New hotels",
|
||||
href: "/new-hotels",
|
||||
id: "new-hotels",
|
||||
openInNewTab: false,
|
||||
isExternal: false,
|
||||
},
|
||||
{
|
||||
title: "Accessibililty",
|
||||
href: "/accessibility",
|
||||
id: "accessibility",
|
||||
openInNewTab: false,
|
||||
isExternal: false,
|
||||
},
|
||||
{
|
||||
title: "Sustanability",
|
||||
href: "/sustainability",
|
||||
id: "sustainability",
|
||||
openInNewTab: false,
|
||||
isExternal: false,
|
||||
},
|
||||
],
|
||||
appDownloads: {
|
||||
title: "Scandic App",
|
||||
links: [
|
||||
{
|
||||
title: "App Store",
|
||||
href: "https://apps.apple.com/se/app/scandic-hotels/id1020208712",
|
||||
id: "apple",
|
||||
},
|
||||
{
|
||||
title: "Google Play",
|
||||
href: "https://play.google.com/store/apps/details?id=com.scandichotels.scandichotels",
|
||||
id: "google",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
secondaryLinks: [
|
||||
{
|
||||
title: "Customer service",
|
||||
links: [
|
||||
{
|
||||
title: "Contact us",
|
||||
href: "/contact-us",
|
||||
id: "contact-us",
|
||||
openInNewTab: false,
|
||||
isExternal: false,
|
||||
},
|
||||
{
|
||||
title: "Frequntly asked questions",
|
||||
href: "/frequently-asked-questions",
|
||||
id: "frequently-asked-questions",
|
||||
openInNewTab: false,
|
||||
isExternal: false,
|
||||
},
|
||||
{
|
||||
title: "Rates and policys",
|
||||
href: "/rates-and-policies",
|
||||
id: "rates-and-policies",
|
||||
openInNewTab: false,
|
||||
isExternal: false,
|
||||
},
|
||||
{
|
||||
title: "Terms and conditions",
|
||||
href: "/terms-and-conditions",
|
||||
id: "terms-and-conditions",
|
||||
openInNewTab: false,
|
||||
isExternal: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "About Scandic Hotels",
|
||||
links: [
|
||||
{
|
||||
title: "Scandic Group",
|
||||
href: "/scandic-group",
|
||||
id: "scandic-group",
|
||||
openInNewTab: false,
|
||||
isExternal: false,
|
||||
},
|
||||
{
|
||||
title: "Investors",
|
||||
href: "/investors",
|
||||
id: "investors",
|
||||
openInNewTab: false,
|
||||
isExternal: false,
|
||||
},
|
||||
{
|
||||
title: "Press",
|
||||
href: "/press",
|
||||
id: "press",
|
||||
openInNewTab: false,
|
||||
isExternal: false,
|
||||
},
|
||||
{
|
||||
title: "Sponsors",
|
||||
href: "/sponsors",
|
||||
id: "sponsors",
|
||||
openInNewTab: false,
|
||||
isExternal: false,
|
||||
},
|
||||
{
|
||||
title: "Partners",
|
||||
href: "/partners",
|
||||
id: "partners",
|
||||
openInNewTab: false,
|
||||
isExternal: false,
|
||||
},
|
||||
{
|
||||
title: "Career",
|
||||
href: "/career",
|
||||
id: "career",
|
||||
openInNewTab: false,
|
||||
isExternal: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
copyrightCompany: "Scandic AB",
|
||||
copyrightInfo: "All rights reserved.",
|
||||
socialMedia: {
|
||||
links: [
|
||||
{
|
||||
title: "Facebook",
|
||||
href: "https://www.facebook.com/scandichotels/",
|
||||
id: "facebook",
|
||||
},
|
||||
{
|
||||
title: "Instagram",
|
||||
href: "https://www.instagram.com/scandichotels/",
|
||||
id: "instagram",
|
||||
},
|
||||
{
|
||||
title: "Tripadvisor",
|
||||
href: "https://www.tripadvisor.com/Hotel_Review-g297628-d1020208712-Reviews-Scandic_Hotels-Stockholm_Sweden.html",
|
||||
id: "tripadvisor",
|
||||
},
|
||||
],
|
||||
},
|
||||
tertiaryLinks: [
|
||||
{
|
||||
title: "Cookies",
|
||||
href: "/cookies",
|
||||
id: "cookies",
|
||||
},
|
||||
{
|
||||
title: "Privacy policy",
|
||||
href: "/privacy",
|
||||
id: "privacy",
|
||||
},
|
||||
],
|
||||
languageSwitcher: {
|
||||
urls: {
|
||||
da: {
|
||||
url: "https://www.scandichotels.com/da",
|
||||
isExternal: true,
|
||||
},
|
||||
de: {
|
||||
url: "https://www.scandichotels.com/de",
|
||||
isExternal: true,
|
||||
},
|
||||
en: {
|
||||
url: "https://www.scandichotels.com/en",
|
||||
isExternal: true,
|
||||
},
|
||||
fi: {
|
||||
url: "https://www.scandichotels.com/fi",
|
||||
isExternal: true,
|
||||
},
|
||||
no: {
|
||||
url: "https://www.scandichotels.com/no",
|
||||
isExternal: true,
|
||||
},
|
||||
sv: {
|
||||
url: "https://www.scandichotels.com/sv",
|
||||
isExternal: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import NavigationMenu from "../NavigationMenu"
|
||||
|
||||
import styles from "./mobileMenu.module.css"
|
||||
|
||||
import { DropdownTypeEnum } from "@/types/components/dropdown/dropdown"
|
||||
import type { MobileMenuProps } from "@/types/components/header/mobileMenu"
|
||||
|
||||
export default function MobileMenu({
|
||||
@@ -23,37 +24,25 @@ export default function MobileMenu({
|
||||
}: MobileMenuProps) {
|
||||
const intl = useIntl()
|
||||
const {
|
||||
handleHamburgerClick,
|
||||
toggleDropdown,
|
||||
isHamburgerMenuOpen,
|
||||
isMyPagesMobileMenuOpen,
|
||||
isLanguageSwitcherOpen,
|
||||
toggleHamburgerMenu,
|
||||
toggleMyPagesMobileMenu,
|
||||
toggleLanguageSwitcher,
|
||||
isHeaderLanguageSwitcherMobileOpen,
|
||||
isFooterLanguageSwitcherOpen,
|
||||
} = useDropdownStore()
|
||||
|
||||
useHandleKeyUp((event: KeyboardEvent) => {
|
||||
if (event.key === "Escape" && isHamburgerMenuOpen) {
|
||||
toggleHamburgerMenu()
|
||||
toggleDropdown(DropdownTypeEnum.HamburgerMenu)
|
||||
}
|
||||
})
|
||||
|
||||
function handleHamburgerClick() {
|
||||
if (isMyPagesMobileMenuOpen) {
|
||||
toggleMyPagesMobileMenu()
|
||||
} else {
|
||||
if (isLanguageSwitcherOpen) {
|
||||
toggleLanguageSwitcher()
|
||||
}
|
||||
|
||||
toggleHamburgerMenu()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
className={`${styles.hamburger} ${isHamburgerMenuOpen || isMyPagesMobileMenuOpen ? styles.isExpanded : ""}`}
|
||||
className={`${styles.hamburger} ${isHamburgerMenuOpen || isMyPagesMobileMenuOpen || isFooterLanguageSwitcherOpen || isHeaderLanguageSwitcherMobileOpen ? styles.isExpanded : ""}`}
|
||||
aria-label={intl.formatMessage({
|
||||
id: isHamburgerMenuOpen ? "Close menu" : "Open menu",
|
||||
})}
|
||||
@@ -61,7 +50,10 @@ export default function MobileMenu({
|
||||
>
|
||||
<span className={styles.bar}></span>
|
||||
</button>
|
||||
<Modal className={styles.modal} isOpen={isHamburgerMenuOpen}>
|
||||
<Modal
|
||||
className={styles.modal}
|
||||
isOpen={isHamburgerMenuOpen || isHeaderLanguageSwitcherMobileOpen}
|
||||
>
|
||||
<Dialog
|
||||
className={styles.dialog}
|
||||
aria-label={intl.formatMessage({ id: "Menu" })}
|
||||
|
||||
@@ -7,7 +7,6 @@ import useDropdownStore from "@/stores/main-menu"
|
||||
import { ChevronDownIcon } from "@/components/Icons"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import { useHandleKeyUp } from "@/hooks/useHandleKeyUp"
|
||||
import useLang from "@/hooks/useLang"
|
||||
import { getInitials } from "@/utils/user"
|
||||
|
||||
import Avatar from "../Avatar"
|
||||
@@ -16,6 +15,7 @@ import MyPagesMenuContent from "../MyPagesMenuContent"
|
||||
|
||||
import styles from "./myPagesMenu.module.css"
|
||||
|
||||
import { DropdownTypeEnum } from "@/types/components/dropdown/dropdown"
|
||||
import type { MyPagesMenuProps } from "@/types/components/header/myPagesMenu"
|
||||
|
||||
export default function MyPagesMenu({
|
||||
@@ -24,18 +24,21 @@ export default function MyPagesMenu({
|
||||
user,
|
||||
}: MyPagesMenuProps) {
|
||||
const intl = useIntl()
|
||||
const lang = useLang()
|
||||
const { toggleMyPagesMenu, isMyPagesMenuOpen } = useDropdownStore()
|
||||
|
||||
const { toggleDropdown, isMyPagesMenuOpen } = useDropdownStore()
|
||||
|
||||
useHandleKeyUp((event: KeyboardEvent) => {
|
||||
if (event.key === "Escape" && isMyPagesMenuOpen) {
|
||||
toggleMyPagesMenu()
|
||||
toggleDropdown(DropdownTypeEnum.MyPagesMenu)
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={styles.myPagesMenu}>
|
||||
<MainMenuButton className={styles.button} onClick={toggleMyPagesMenu}>
|
||||
<MainMenuButton
|
||||
className={styles.button}
|
||||
onClick={() => toggleDropdown(DropdownTypeEnum.MyPagesMenu)}
|
||||
>
|
||||
<Avatar initials={getInitials(user.firstName, user.lastName)} />
|
||||
<Subtitle type="two" className={styles.userName}>
|
||||
{intl.formatMessage({ id: "Hi" })} {user.firstName}!
|
||||
@@ -51,7 +54,9 @@ export default function MyPagesMenu({
|
||||
navigation={navigation}
|
||||
user={user}
|
||||
membership={membership}
|
||||
toggleOpenStateFn={toggleMyPagesMenu}
|
||||
toggleOpenStateFn={() =>
|
||||
toggleDropdown(DropdownTypeEnum.MyPagesMenu)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
@@ -14,6 +14,7 @@ import MyPagesMenuContent from "../MyPagesMenuContent"
|
||||
|
||||
import styles from "./myPagesMobileMenu.module.css"
|
||||
|
||||
import { DropdownTypeEnum } from "@/types/components/dropdown/dropdown"
|
||||
import type { MyPagesMenuProps } from "@/types/components/header/myPagesMenu"
|
||||
|
||||
export default function MyPagesMobileMenu({
|
||||
@@ -22,12 +23,11 @@ export default function MyPagesMobileMenu({
|
||||
user,
|
||||
}: MyPagesMenuProps) {
|
||||
const intl = useIntl()
|
||||
const { toggleMyPagesMobileMenu, isMyPagesMobileMenuOpen } =
|
||||
useDropdownStore()
|
||||
const { isMyPagesMobileMenuOpen, toggleDropdown } = useDropdownStore()
|
||||
|
||||
useHandleKeyUp((event: KeyboardEvent) => {
|
||||
if (event.key === "Escape" && isMyPagesMobileMenuOpen) {
|
||||
toggleMyPagesMobileMenu()
|
||||
toggleDropdown(DropdownTypeEnum.MyPagesMobileMenu)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -35,7 +35,7 @@ export default function MyPagesMobileMenu({
|
||||
<div className={styles.myPagesMobileMenu}>
|
||||
<MainMenuButton
|
||||
className={styles.button}
|
||||
onClick={toggleMyPagesMobileMenu}
|
||||
onClick={() => toggleDropdown(DropdownTypeEnum.MyPagesMobileMenu)}
|
||||
aria-label={intl.formatMessage({ id: "Open my pages menu" })}
|
||||
>
|
||||
<Avatar initials={getInitials(user.firstName, user.lastName)} />
|
||||
@@ -49,7 +49,9 @@ export default function MyPagesMobileMenu({
|
||||
membership={membership}
|
||||
navigation={navigation}
|
||||
user={user}
|
||||
toggleOpenStateFn={toggleMyPagesMobileMenu}
|
||||
toggleOpenStateFn={() =>
|
||||
toggleDropdown(DropdownTypeEnum.MyPagesMobileMenu)
|
||||
}
|
||||
/>
|
||||
</Dialog>
|
||||
</Modal>
|
||||
|
||||
@@ -13,6 +13,7 @@ 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"
|
||||
|
||||
export default function LanguageSwitcherContent({
|
||||
@@ -21,9 +22,13 @@ export default function LanguageSwitcherContent({
|
||||
}: LanguageSwitcherProps) {
|
||||
const intl = useIntl()
|
||||
const currentLanguage = useLang()
|
||||
const { toggleLanguageSwitcher } = useDropdownStore()
|
||||
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}>
|
||||
@@ -32,7 +37,7 @@ export default function LanguageSwitcherContent({
|
||||
<button
|
||||
type="button"
|
||||
className={styles.backButton}
|
||||
onClick={toggleLanguageSwitcher}
|
||||
onClick={() => toggleDropdown(position)}
|
||||
>
|
||||
<ChevronLeftIcon color="red" />
|
||||
<Subtitle type="one">Main Menu</Subtitle>
|
||||
|
||||
@@ -10,9 +10,11 @@ import { useHandleKeyUp } from "@/hooks/useHandleKeyUp"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
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"
|
||||
|
||||
export default function LanguageSwitcher({
|
||||
@@ -21,16 +23,37 @@ export default function LanguageSwitcher({
|
||||
}: LanguageSwitcherProps) {
|
||||
const intl = useIntl()
|
||||
const currentLanguage = useLang()
|
||||
const { toggleLanguageSwitcher, isLanguageSwitcherOpen } = useDropdownStore()
|
||||
const {
|
||||
toggleDropdown,
|
||||
isFooterLanguageSwitcherOpen,
|
||||
isHeaderLanguageSwitcherOpen,
|
||||
isHeaderLanguageSwitcherMobileOpen,
|
||||
} = useDropdownStore()
|
||||
|
||||
const position = type === "footer" ? "footer" : "header"
|
||||
const color = type === "footer" ? "pale" : "burgundy"
|
||||
|
||||
const dropdownType = {
|
||||
footer: DropdownTypeEnum.FooterLanguageSwitcher,
|
||||
desktopHeader: DropdownTypeEnum.HeaderLanguageSwitcher,
|
||||
mobileHeader: DropdownTypeEnum.HeaderLanguageSwitcherMobile,
|
||||
}[type]
|
||||
|
||||
const isLanguageSwitcherOpen =
|
||||
(type === "footer" && isFooterLanguageSwitcherOpen) ||
|
||||
(type !== "footer" &&
|
||||
(isHeaderLanguageSwitcherOpen || isHeaderLanguageSwitcherMobileOpen))
|
||||
|
||||
useHandleKeyUp((event: KeyboardEvent) => {
|
||||
if (event.key === "Escape" && isLanguageSwitcherOpen) {
|
||||
toggleLanguageSwitcher()
|
||||
toggleDropdown(dropdownType)
|
||||
}
|
||||
})
|
||||
|
||||
const classNames = languageSwitcherVariants({ color, position })
|
||||
|
||||
return (
|
||||
<div className={styles.languageSwitcher}>
|
||||
<div className={classNames}>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.button}
|
||||
@@ -39,15 +62,15 @@ export default function LanguageSwitcher({
|
||||
? "Close language menu"
|
||||
: "Open language menu",
|
||||
})}
|
||||
onClick={toggleLanguageSwitcher}
|
||||
onClick={() => toggleDropdown(dropdownType)}
|
||||
>
|
||||
<GlobeIcon width={20} height={20} color="burgundy" />
|
||||
<GlobeIcon width={20} height={20} color={color} />
|
||||
<span>{languages[currentLanguage]}</span>
|
||||
<ChevronDownIcon
|
||||
className={`${styles.chevron} ${isLanguageSwitcherOpen ? styles.isExpanded : ""}`}
|
||||
width={20}
|
||||
height={20}
|
||||
color="burgundy"
|
||||
color={color}
|
||||
/>
|
||||
</button>
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
.button {
|
||||
background-color: transparent;
|
||||
color: var(--Base-Text-High-contrast);
|
||||
font-family: var(--typography-Caption-Regular-fontFamily);
|
||||
font-size: var(--typography-Caption-Regular-fontSize);
|
||||
border-width: 0;
|
||||
@@ -13,6 +12,14 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.burgundy .button {
|
||||
color: var(--Base-Text-High-contrast);
|
||||
}
|
||||
|
||||
.pale .button {
|
||||
color: var(--Primary-Dark-On-Surface-Text);
|
||||
}
|
||||
|
||||
.chevron {
|
||||
justify-self: end;
|
||||
transition: transform 0.3s;
|
||||
@@ -45,29 +52,43 @@
|
||||
|
||||
.dropdown {
|
||||
position: absolute;
|
||||
top: 2.25rem;
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
border-radius: var(--Corner-radius-Large);
|
||||
box-shadow: 0px 0px 14px 6px #0000001a;
|
||||
display: none;
|
||||
min-width: 12.5rem;
|
||||
z-index: 1;
|
||||
}
|
||||
.top .dropdown {
|
||||
top: 2.25rem;
|
||||
bottom: auto;
|
||||
}
|
||||
|
||||
/* Triangle above dropdown */
|
||||
.top .dropdown::before {
|
||||
top: -1.25rem;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* Triangle dropdown */
|
||||
.dropdown::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -1.25rem;
|
||||
right: 2.4rem;
|
||||
transform: rotate(180deg);
|
||||
border-width: 0.75rem;
|
||||
border-style: solid;
|
||||
border-color: var(--Base-Surface-Primary-light-Normal) transparent
|
||||
transparent transparent;
|
||||
}
|
||||
|
||||
.bottom .dropdown {
|
||||
top: auto;
|
||||
bottom: 2.25rem;
|
||||
}
|
||||
|
||||
.bottom .dropdown::before {
|
||||
top: 100%;
|
||||
}
|
||||
|
||||
.button {
|
||||
grid-template-columns: repeat(3, max-content);
|
||||
font-size: var(--typography-Body-Bold-fontSize);
|
||||
|
||||
20
components/LanguageSwitcher/variants.ts
Normal file
20
components/LanguageSwitcher/variants.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { cva } from "class-variance-authority"
|
||||
|
||||
import styles from "./languageSwitcher.module.css"
|
||||
|
||||
export const languageSwitcherVariants = cva(styles.languageSwitcher, {
|
||||
variants: {
|
||||
color: {
|
||||
burgundy: styles.burgundy,
|
||||
pale: styles.pale,
|
||||
},
|
||||
position: {
|
||||
header: styles.top,
|
||||
footer: styles.bottom,
|
||||
},
|
||||
defaultVariants: {
|
||||
color: "burgundy",
|
||||
position: "top",
|
||||
},
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user