Merged in feat/mypages-mobile-dropdown (pull request #256)

Feat/mypages mobile dropdown

Approved-by: Michael Zetterberg
This commit is contained in:
Chuma Mcphoy (We Ahead)
2024-06-19 15:13:52 +00:00
committed by Michael Zetterberg
23 changed files with 432 additions and 81 deletions
@@ -31,7 +31,7 @@
text-decoration: underline;
}
@media screen and (min-width: 950px) {
@media screen and (min-width: 1367px) {
.button {
font-weight: 600;
font-size: 16px;
+34 -11
View File
@@ -4,8 +4,10 @@ import { useIntl } from "react-intl"
import { login } from "@/constants/routes/handleAuth"
import { myPages } from "@/constants/routes/myPages"
import useDropdownStore from "@/stores/main-menu"
import Image from "@/components/Image"
import Avatar from "@/components/MyPages/Avatar"
import Link from "@/components/TempDesignSystem/Link"
import BookingButton from "../BookingButton"
@@ -21,16 +23,19 @@ export function MainMenu({
logo,
topMenuMobileLinks,
languageSwitcher,
myPagesMobileDropdown,
bookingHref,
isLoggedIn,
user,
lang,
}: MainMenuProps) {
const intl = useIntl()
const [isOpen, setIsOpen] = useState(false)
function toogleIsOpen() {
setIsOpen((prevIsOpen) => !prevIsOpen)
}
const {
isHamburgerMenuOpen,
isMyPagesMobileMenuOpen,
toggleHamburgerMenu,
toggleMyPagesMobileMenu,
} = useDropdownStore()
return (
<div className={styles.mainMenu}>
@@ -43,8 +48,8 @@ export function MainMenu({
<nav className={styles.navBar}>
<button
aria-pressed="false"
className={`${styles.expanderBtn} ${isOpen ? styles.expanded : ""}`}
onClick={toogleIsOpen}
className={`${styles.expanderBtn} ${isHamburgerMenuOpen ? styles.expanded : ""}`}
onClick={toggleHamburgerMenu}
type="button"
>
<span className={styles.iconBars}></span>
@@ -66,13 +71,16 @@ export function MainMenu({
</a>
<ul
className={`${styles.listWrapper} ${isOpen ? styles.isOpen : ""}`}
className={`${styles.listWrapper} ${isHamburgerMenuOpen ? styles.isOpen : ""}`}
>
<ul className={styles.linkRow}>
{isLoggedIn ? (
{!!user ? (
<>
<li>
<div className={styles.loggedInLogo} />
<li className={styles.avatarWrapper}>
<Avatar
firstName={user.firstName}
lastName={user.lastName}
/>
</li>
<li className={styles.mobileLinkRow}>
<Link
@@ -139,8 +147,23 @@ export function MainMenu({
) : null}
</ul>
<div className={styles.buttonContainer}>
<div className={styles.myPagesDesktopLink}>
<Link className={styles.link} href={myPages[lang]}>
{intl.formatMessage({ id: "My pages" })}
</Link>
</div>
<BookingButton href={bookingHref} />
{myPagesMobileDropdown && user ? (
<div
role="button"
onClick={() => toggleMyPagesMobileMenu()}
className={styles.avatarButton}
>
<Avatar firstName={user.firstName} lastName={user.lastName} />
</div>
) : null}
</div>
{isMyPagesMobileMenuOpen ? myPagesMobileDropdown : null}
</nav>
</div>
</div>
@@ -1,21 +1,20 @@
.mainMenu {
background-color: #fff;
background-color: var(--Main-Grey-White);
background-image: none;
box-shadow: 0 0 7px rgba(0, 0, 0, 0.75);
box-shadow: 0px 1.001px 1.001px 0px rgba(0, 0, 0, 0.05);
max-height: 100%;
overflow: visible;
position: fixed;
top: 0;
width: 100%;
z-index: 99999;
height: 52.39px;
height: 70.047px;
}
.container {
box-sizing: content-box;
height: 100%;
margin: 0 auto;
max-width: 1200px;
padding: 0;
}
@@ -26,8 +25,11 @@
.navBar {
display: grid;
grid-template-columns: 1fr 80px 1fr;
grid-template-columns: auto auto 1fr auto;
grid-template-areas: "expanderBtn logoLink . buttonContainer";
grid-template-rows: 100%;
height: 100%;
padding: 0 var(--Spacing-x2);
}
.expanderBtn {
@@ -46,7 +48,7 @@
background: #757575;
border-radius: 2.3px;
display: inline-block;
height: 5px;
height: 3px;
position: relative;
transition: 0.3s;
width: 32px;
@@ -98,16 +100,19 @@
}
.logoLink {
display: inline;
/*padding: 16px 0 8px;*/
display: inline-flex;
align-items: center;
height: 100%;
width: 80px;
padding: 16px 0 8px;
padding-left: var(--Spacing-x1);
}
.logo {
width: 80px;
object-fit: fill;
}
.listWrapper {
background-color: #fff;
border-top: 1px solid #e3e0db;
@@ -175,13 +180,8 @@
padding: 15px 15px 15px 5px;
}
.loggedInLogo {
height: 35px;
width: 35px;
border-radius: 50px;
background-color: #000;
.avatarWrapper {
margin-right: 4px;
margin-left: -4px;
}
.mobileLinkButton {
@@ -228,50 +228,56 @@
}
.buttonContainer {
display: flex;
display: inline-flex;
justify-content: flex-end;
align-items: center;
margin-right: 8px;
gap: var(--Spacing-x3);
}
@media screen and (min-width: 950px) {
.myPagesDesktopLink {
display: none;
}
@media (min-width: 1367px) {
.navBar {
grid-template-columns: 140px auto 1fr;
height: 82.4px;
align-content: center;
padding: 0px 0px var(--Spacing-x-quarter) 0px;
overflow: hidden;
}
.logoLink {
display: inline-block;
width: 100%;
padding: 27px 30px 26px 0;
text-align: center;
align-items: center;
}
.mainMenu {
box-shadow: none;
background-color: hsla(0, 0%, 100%, 0.95);
position: relative;
z-index: unset;
height: 85.09px;
height: 82.4px;
}
.container {
padding: 0px 30px;
padding: 0 var(--Spacing-x5) 0 120px;
}
.mainLinks {
padding-top: 2.5px;
background-color: transparent;
}
.navBar {
grid-template-columns: 132.18px 1fr auto;
align-content: center;
padding-bottom: 2px;
overflow: hidden;
height: 100%;
}
.expanderBtn {
display: none;
}
.logoLink {
display: flex;
height: 100%;
padding: 30px 10px 30px 15px;
width: auto;
align-items: center;
}
.logo {
width: 102.17px;
height: 100%;
@@ -282,9 +288,11 @@
border-top: none;
display: flex;
align-items: center;
padding-bottom: 0;
padding-top: 0;
position: static;
width: 100%;
padding-bottom: 0px;
height: 100%;
}
.listWrapper.isOpen {
@@ -292,7 +300,7 @@
}
.li {
display: table-cell;
display: inline-grid;
float: none;
vertical-align: middle;
line-height: 1.15;
@@ -300,10 +308,16 @@
.link {
background-image: none;
font-family: Helvetica, Arial, sans-serif;
font-weight: 700;
line-height: 1.15;
font-family: var(--typography-Body-Regular-fontFamily);
font-size: var(--typography-Body-Regular-fontSize);
font-feature-settings:
"clig" off,
"liga" off;
font-weight: 600;
line-height: 125%;
padding: 30px 15px;
text-transform: uppercase;
color: var(--text-black); /* Design system should return #404040 */
}
.linkRow {
@@ -322,25 +336,11 @@
.buttonContainer {
margin-right: 0;
}
}
@media (min-width: 1200px) {
.mainMenu {
height: 82.4px;
.avatarButton {
display: none;
}
.navBar {
grid-template-columns: 140px auto 1fr;
height: 82.4px;
}
.logoLink {
display: inline-block;
width: 100%;
padding: 27px 30px 26px 0;
text-align: center;
}
.listWrapper {
padding-top: 0;
.myPagesDesktopLink {
display: block;
}
}
@@ -0,0 +1,80 @@
"use client"
import { Fragment } from "react"
import { useIntl } from "react-intl"
import { Lang } from "@/constants/languages"
import { logout } from "@/constants/routes/handleAuth"
import { navigationQueryRouter } from "@/server/routers/contentstack/myPages/navigation/query"
import useDropdownStore from "@/stores/main-menu"
import Divider from "@/components/TempDesignSystem/Divider"
import Link from "@/components/TempDesignSystem/Link"
import Title from "@/components/TempDesignSystem/Text/Title"
import styles from "./my-pages-mobile-dropdown.module.css"
type Navigation = Awaited<ReturnType<(typeof navigationQueryRouter)["get"]>>
export default function MyPagesMobileDropdown({
navigation,
lang,
}: {
navigation: Navigation
lang: Lang | null
}) {
const { formatMessage } = useIntl()
const { toggleMyPagesMobileMenu, isMyPagesMobileMenuOpen } =
useDropdownStore()
if (!navigation) {
return null
}
return (
<nav
className={`${styles.navigationMenu} ${isMyPagesMobileMenuOpen ? styles.navigationMenuIsOpen : ""}`}
>
<Title className={styles.heading} textTransform="capitalize" level="h5">
{navigation.title}
</Title>
{navigation.menuItems.map((menuItem, idx) => (
<Fragment key={`${menuItem.display_sign_out_link}-${idx}`}>
<div className={styles.dividerWrapper}>
<Divider color="subtle" />
</div>
<ul className={styles.dropdownWrapper}>
<ul className={styles.dropdownLinks}>
{menuItem.links.map((link) => (
<li key={link.uid}>
<Link
href={link.originalUrl || link.url}
partialMatch
size={menuItem.display_sign_out_link ? "small" : "regular"}
variant="myPageMobileDropdown"
color="burgundy"
onClick={toggleMyPagesMobileMenu}
>
{link.linkText}
</Link>
</li>
))}
{menuItem.display_sign_out_link && lang ? (
<li>
<Link
color="burgundy"
href={logout[lang]}
prefetch={false}
size="small"
variant="sidebar"
>
{formatMessage({ id: "Log out" })}
</Link>
</li>
) : null}
</ul>
</ul>
</Fragment>
))}
</nav>
)
}
@@ -0,0 +1,63 @@
.navigationMenu {
background-color: #fff;
border-top: 1px solid #e3e0db;
display: none;
list-style: none;
overflow-y: visible;
margin: 0;
padding-inline-start: 0;
}
.navigationMenu.navigationMenuIsOpen {
display: block;
left: 0;
position: absolute;
right: 0;
top: 100%;
}
.dropdownWrapper {
display: flex;
width: 100%;
padding: 20px var(--Spacing-x2);
flex-direction: column;
justify-content: center;
align-items: flex-start;
background-color: var(--Main-Grey-White);
box-shadow:
0px 276px 77px 0px rgba(0, 0, 0, 0),
0px 177px 71px 0px rgba(0, 0, 0, 0.01),
0px 99px 60px 0px rgba(0, 0, 0, 0.05),
0px 44px 44px 0px rgba(0, 0, 0, 0.09),
0px 11px 24px 0px rgba(0, 0, 0, 0.1);
}
.dividerWrapper {
background-color: var(--Main-Grey-White);
padding: 0 var(--Spacing-x2);
margin: auto;
place-content: center;
display: flex;
}
.heading {
padding: 20px var(--Spacing-x2);
}
.dropdownLinks {
display: flex;
flex-direction: column;
gap: var(--Spacing-x-half);
width: 100%;
list-style: none;
}
@media screen and (min-width: 1367px) {
.navigationMenu {
display: none;
}
.navigationMenu.navigationMenuIsOpen {
display: none;
}
}
+2 -2
View File
@@ -2,8 +2,8 @@
display: grid;
}
@media screen and (max-width: 950px) {
@media screen and (max-width: 1366px) {
.header {
height: 50px;
height: 70.047px;
}
}
+14 -6
View File
@@ -15,11 +15,18 @@ import { LangParams } from "@/types/params"
export default async function Header({
lang,
languageSwitcher,
}: LangParams & { languageSwitcher: React.ReactNode }) {
const data = await serverClient().contentstack.base.header({
lang,
})
const session = await auth()
myPagesMobileDropdown,
}: LangParams & { languageSwitcher: React.ReactNode } & {
myPagesMobileDropdown: React.ReactNode
}) {
const [data, session] = await Promise.all([
serverClient().contentstack.base.header({
lang,
}),
auth(),
])
const user = !!session ? await serverClient().user.get() : null
if (!data) {
return null
@@ -49,8 +56,9 @@ export default async function Header({
logo={logo}
topMenuMobileLinks={topMenuMobileLinks}
languageSwitcher={languageSwitcher}
myPagesMobileDropdown={myPagesMobileDropdown}
bookingHref={homeHref}
isLoggedIn={!!session}
user={user}
lang={lang}
/>
</header>
@@ -0,0 +1,27 @@
.avatar {
display: inline-flex;
align-items: center;
justify-content: center;
vertical-align: middle;
overflow: hidden;
cursor: pointer;
width: 35px;
height: 35px;
border-radius: 100%;
background-color: rgba(0, 0, 0, 0.05);
}
.avatarInitialsText {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--Theme-Primary-Dark-Surface-Normal);
color: var(--Theme-Primary-Dark-On-Surface-Text);
font-size: var(--typography-Caption-Bold-fontSize);
font-family: var(--typography-Body-Regular-fontFamily);
font-weight: var(--typography-Caption-Bold-fontWeight);
line-height: 150%;
letter-spacing: 0.096px;
}
+20
View File
@@ -0,0 +1,20 @@
import { getInitials } from "@/utils/user"
import styles from "./avatar.module.css"
import { User } from "@/types/user"
export default function Avatar({
firstName,
lastName,
}: {
firstName: User["firstName"]
lastName: User["lastName"]
}) {
const initials = getInitials(firstName, lastName)
return (
<span className={styles.avatar}>
<span className={styles.avatarInitialsText}>{initials}</span>
</span>
)
}
@@ -17,6 +17,10 @@
border-bottom-color: var(--Theme-Primary-Light-On-Surface-Divider);
}
.subtle {
border-bottom-color: var(--Base-Border-Subtle);
}
.opacity100 {
opacity: 1;
}
@@ -7,6 +7,7 @@ export const dividerVariants = cva(styles.divider, {
color: {
burgundy: styles.burgundy,
peach: styles.peach,
subtle: styles.subtle,
},
opacity: {
100: styles.opacity100,
@@ -29,6 +29,33 @@
text-decoration: underline;
}
.myPageMobileDropdown {
color: var(--Scandic-Brand-Burgundy);
font-family: var(--typography-Body-Regular-fontFamily);
font-size: var(--typography-Body-Regular-fontSize);
line-height: var(--typography-Body-Regular-lineHeight);
letter-spacing: 0.096px;
padding: var(--Spacing-x1) var(--Spacing-x1) var(--Spacing-x1)
var(--Spacing-x-one-and-half);
width: 100%;
border-radius: var(--Corner-radius-Medium);
gap: 4px;
display: flex;
align-items: center;
gap: 4px;
align-self: stretch;
}
.myPageMobileDropdown.active {
background-color: var(--Scandic-Brand-Pale-Peach);
border-radius: var(--Corner-radius-Medium);
font-family: var(--typography-Body-Underlined-fontFamily);
font-size: var(--typography-Body-Underlined-fontSize);
font-weight: var(--typography-Body-Underlined-fontWeight);
letter-spacing: var(--typography-Body-Underlined-letterSpacing);
line-height: var(--typography-Body-Underlined-lineHeight);
}
.shortcut {
align-items: center;
border-bottom: 0.5px solid var(--Scandic-Beige-20);
@@ -23,6 +23,7 @@ export const linkVariants = cva(styles.link, {
default: styles.default,
icon: styles.icon,
myPage: styles.myPage,
myPageMobileDropdown: styles.myPageMobileDropdown,
shortcut: styles.shortcut,
sidebar: styles.sidebar,
},
@@ -68,6 +68,10 @@
text-decoration: var(--typography-Title-5-textDecoration);
}
.capitalize {
text-transform: capitalize;
}
.regular {
text-transform: none;
}
@@ -16,6 +16,7 @@ const config = {
left: styles.left,
},
textTransform: {
capitalize: styles.capitalize,
regular: styles.regular,
uppercase: styles.uppercase,
},