From 08cde7ae2f3b2d1bb69845100c8d5defab804243 Mon Sep 17 00:00:00 2001 From: Erik Tiekstra Date: Mon, 19 Aug 2024 12:52:35 +0200 Subject: [PATCH] feat(SW-184): added main components for new header --- .../(live)/@header/[...paths]/layout.tsx | 18 +-- components/Header/MainMenu/Menu/index.tsx | 18 +++ .../Header/MainMenu/Menu/menu.module.css | 8 ++ components/Header/MainMenu/Menu/menu.ts | 5 + components/Header/MainMenu/MenuItem/index.tsx | 37 ++++++ .../MainMenu/MenuItem/menuItem.module.css | 21 +++ .../Header/MainMenu/MenuItem/menuItem.ts | 5 + .../MainMenu/User/Avatar/avatar.module.css | 16 +++ .../Header/MainMenu/User/Avatar/avatar.ts | 6 + .../Header/MainMenu/User/Avatar/index.tsx | 19 +++ components/Header/MainMenu/User/index.tsx | 16 +++ .../Header/MainMenu/User/user.module.css | 12 ++ components/Header/MainMenu/index.tsx | 125 ++++++++++++++++++ .../Header/MainMenu/mainMenu.module.css | 15 +++ .../Header/TopMenu/Button/button.module.css | 9 ++ components/Header/TopMenu/Button/button.ts | 2 + components/Header/TopMenu/Button/index.tsx | 11 ++ .../Header/TopMenu/LanguageSwitcher/index.tsx | 30 +++++ .../languageSwitcher.module.css | 17 +++ components/Header/TopMenu/Search/index.tsx | 12 ++ .../Header/TopMenu/Search/search.module.css | 9 ++ components/Header/TopMenu/index.tsx | 24 ++++ components/Header/TopMenu/topMenu.module.css | 18 +++ components/Header/header.module.css | 4 + components/Header/index.tsx | 13 ++ components/Icons/Gift.tsx | 36 +++++ components/Icons/Person.tsx | 22 +-- components/Icons/Search.tsx | 36 +++++ components/Icons/get-icon-by-icon-name.ts | 6 + components/Icons/index.tsx | 2 + types/components/icon.ts | 2 + 31 files changed, 548 insertions(+), 26 deletions(-) create mode 100644 components/Header/MainMenu/Menu/index.tsx create mode 100644 components/Header/MainMenu/Menu/menu.module.css create mode 100644 components/Header/MainMenu/Menu/menu.ts create mode 100644 components/Header/MainMenu/MenuItem/index.tsx create mode 100644 components/Header/MainMenu/MenuItem/menuItem.module.css create mode 100644 components/Header/MainMenu/MenuItem/menuItem.ts create mode 100644 components/Header/MainMenu/User/Avatar/avatar.module.css create mode 100644 components/Header/MainMenu/User/Avatar/avatar.ts create mode 100644 components/Header/MainMenu/User/Avatar/index.tsx create mode 100644 components/Header/MainMenu/User/index.tsx create mode 100644 components/Header/MainMenu/User/user.module.css create mode 100644 components/Header/MainMenu/index.tsx create mode 100644 components/Header/MainMenu/mainMenu.module.css create mode 100644 components/Header/TopMenu/Button/button.module.css create mode 100644 components/Header/TopMenu/Button/button.ts create mode 100644 components/Header/TopMenu/Button/index.tsx create mode 100644 components/Header/TopMenu/LanguageSwitcher/index.tsx create mode 100644 components/Header/TopMenu/LanguageSwitcher/languageSwitcher.module.css create mode 100644 components/Header/TopMenu/Search/index.tsx create mode 100644 components/Header/TopMenu/Search/search.module.css create mode 100644 components/Header/TopMenu/index.tsx create mode 100644 components/Header/TopMenu/topMenu.module.css create mode 100644 components/Header/header.module.css create mode 100644 components/Header/index.tsx create mode 100644 components/Icons/Gift.tsx create mode 100644 components/Icons/Search.tsx diff --git a/app/[lang]/(live)/@header/[...paths]/layout.tsx b/app/[lang]/(live)/@header/[...paths]/layout.tsx index 3a56b7a6d..17edf6a50 100644 --- a/app/[lang]/(live)/@header/[...paths]/layout.tsx +++ b/app/[lang]/(live)/@header/[...paths]/layout.tsx @@ -1,21 +1,9 @@ -import Header from "@/components/Current/Header" +import Header from "@/components/Header" import { setLang } from "@/i18n/serverContext" import type { LangParams, LayoutArgs } from "@/types/params" -export default function HeaderLayout({ - languageSwitcher, - myPagesMobileDropdown, - params, -}: LayoutArgs & { - languageSwitcher: React.ReactNode - myPagesMobileDropdown: React.ReactNode -}) { +export default function HeaderLayout({ params }: LayoutArgs) { setLang(params.lang) - return ( -
- ) + return
} diff --git a/components/Header/MainMenu/Menu/index.tsx b/components/Header/MainMenu/Menu/index.tsx new file mode 100644 index 000000000..7806cf9ad --- /dev/null +++ b/components/Header/MainMenu/Menu/index.tsx @@ -0,0 +1,18 @@ +import MenuItem from "../MenuItem" +import { MenuProps } from "./menu" + +import styles from "./menu.module.css" + +export default function Menu({ items }: MenuProps) { + function handleButtonClick() {} + + return ( +
    + {items.map((item) => ( +
  • + +
  • + ))} +
+ ) +} diff --git a/components/Header/MainMenu/Menu/menu.module.css b/components/Header/MainMenu/Menu/menu.module.css new file mode 100644 index 000000000..b91ed27a0 --- /dev/null +++ b/components/Header/MainMenu/Menu/menu.module.css @@ -0,0 +1,8 @@ +.menu { + list-style: none; + margin: 0; + display: flex; + justify-content: space-between; + align-items: center; + gap: var(--Spacing-x4); +} diff --git a/components/Header/MainMenu/Menu/menu.ts b/components/Header/MainMenu/Menu/menu.ts new file mode 100644 index 000000000..aae9572ce --- /dev/null +++ b/components/Header/MainMenu/Menu/menu.ts @@ -0,0 +1,5 @@ +import { MenuItem } from ".." + +export interface MenuProps { + items: MenuItem[] +} diff --git a/components/Header/MainMenu/MenuItem/index.tsx b/components/Header/MainMenu/MenuItem/index.tsx new file mode 100644 index 000000000..2c3f0203d --- /dev/null +++ b/components/Header/MainMenu/MenuItem/index.tsx @@ -0,0 +1,37 @@ +"use client" + +import { useState } from "react" + +import { ChevronDownIcon } from "@/components/Icons" +import Link from "@/components/TempDesignSystem/Link" + +import { MenuItemProps } from "./menuItem" + +import styles from "./menuItem.module.css" + +export default function MenuItem({ item }: MenuItemProps) { + const { children, title, href, seeAllLinkText, infoCard } = item + const [isExpanded, setIsExpanded] = useState(false) + + function handleButtonClick() { + setIsExpanded((prev) => !prev) + } + + return children?.length ? ( + + ) : ( + + {title} + + ) +} diff --git a/components/Header/MainMenu/MenuItem/menuItem.module.css b/components/Header/MainMenu/MenuItem/menuItem.module.css new file mode 100644 index 000000000..4b8afc4de --- /dev/null +++ b/components/Header/MainMenu/MenuItem/menuItem.module.css @@ -0,0 +1,21 @@ +.navigationButton { + display: flex; + gap: var(--Spacing-x1); + align-items: center; + background-color: transparent; + color: var(--Base-Text-High-contrast); + border-width: 0; + padding: 0; + cursor: pointer; + font-family: var(--typography-Body-Bold-fontFamily); + font-weight: var(--typography-Body-Bold-fontWeight); + font-size: var(--typography-Body-Bold-fontSize); +} + +.chevron { + transition: transform 0.2s; +} + +.chevron.isExpanded { + transform: rotate(180deg); +} diff --git a/components/Header/MainMenu/MenuItem/menuItem.ts b/components/Header/MainMenu/MenuItem/menuItem.ts new file mode 100644 index 000000000..b4c761110 --- /dev/null +++ b/components/Header/MainMenu/MenuItem/menuItem.ts @@ -0,0 +1,5 @@ +import { MenuItem } from ".." + +export interface MenuItemProps { + item: MenuItem +} diff --git a/components/Header/MainMenu/User/Avatar/avatar.module.css b/components/Header/MainMenu/User/Avatar/avatar.module.css new file mode 100644 index 000000000..0c6055350 --- /dev/null +++ b/components/Header/MainMenu/User/Avatar/avatar.module.css @@ -0,0 +1,16 @@ +.avatar { + display: flex; + justify-content: center; + align-items: center; + overflow: hidden; + border-radius: 50%; + width: 2rem; + height: 2rem; + background-color: var(--Main-Grey-40); +} + +.initials { + font-size: 0.75rem; + color: var(--Base-Text-Inverted); + background-color: var(--Scandic-Peach-70); +} diff --git a/components/Header/MainMenu/User/Avatar/avatar.ts b/components/Header/MainMenu/User/Avatar/avatar.ts new file mode 100644 index 000000000..6156e0bbe --- /dev/null +++ b/components/Header/MainMenu/User/Avatar/avatar.ts @@ -0,0 +1,6 @@ +import { ImageProps } from "next/image" + +export interface AvatarProps { + image?: ImageProps + initials?: string +} diff --git a/components/Header/MainMenu/User/Avatar/index.tsx b/components/Header/MainMenu/User/Avatar/index.tsx new file mode 100644 index 000000000..ceff75675 --- /dev/null +++ b/components/Header/MainMenu/User/Avatar/index.tsx @@ -0,0 +1,19 @@ +import { PersonIcon } from "@/components/Icons" +import Image from "@/components/Image" + +import { AvatarProps } from "./avatar" + +import styles from "./avatar.module.css" + +export default function Avatar({ image, initials }: AvatarProps) { + let classNames = [styles.avatar] + let element = + if (image) { + classNames.push(styles.image) + element = {image.alt} + } else if (initials) { + classNames.push(styles.initials) + element = {initials} + } + return {element} +} diff --git a/components/Header/MainMenu/User/index.tsx b/components/Header/MainMenu/User/index.tsx new file mode 100644 index 000000000..d26bc1173 --- /dev/null +++ b/components/Header/MainMenu/User/index.tsx @@ -0,0 +1,16 @@ +import Link from "next/link" + +import Avatar from "./Avatar" + +import styles from "./user.module.css" + +export default function User() { + return ( +
+ + + Log in/Join + +
+ ) +} diff --git a/components/Header/MainMenu/User/user.module.css b/components/Header/MainMenu/User/user.module.css new file mode 100644 index 000000000..c43fa17fe --- /dev/null +++ b/components/Header/MainMenu/User/user.module.css @@ -0,0 +1,12 @@ +.user { +} + +.link { + display: flex; + gap: var(--Spacing-x1); + align-items: center; + font-family: var(--typography-Body-Bold-fontFamily); + font-weight: 600; + color: var(--Base-Text-High-contrast); + text-decoration: none; +} diff --git a/components/Header/MainMenu/index.tsx b/components/Header/MainMenu/index.tsx new file mode 100644 index 000000000..d0b85ceb4 --- /dev/null +++ b/components/Header/MainMenu/index.tsx @@ -0,0 +1,125 @@ +import Link from "next/link" + +import Image from "@/components/Image" + +import Menu from "./Menu" +import User from "./User" + +import styles from "./mainMenu.module.css" + +export interface MenuItem { + id: string + title: string + href: string + children?: { + groupTitle: string + children: { + id: string + title: string + href: string + }[] + }[] + seeAllLinkText?: string + infoCard?: { + scriptedTitle: string + title: string + description: string + ctaLink: string + } +} + +export default async function MainMenu() { + const menuItems: MenuItem[] = [ + { + id: "hotels", + title: "Hotels", + href: "/hotels", + children: [], + }, + { + id: "business", + title: "Business", + href: "/business", + children: [ + { + groupTitle: "Top conference venues", + children: [ + { + id: "stockholm", + title: "Stockholm", + href: "/stockholm", + }, + { + id: "bergen", + title: "Bergen", + href: "/bergen", + }, + { + id: "copenhagen", + title: "Copenhagen", + href: "/copenhagen", + }, + ], + }, + { + groupTitle: "Scandic for business", + children: [ + { + id: "book-a-venue", + title: "Book a venue", + href: "/book-a-venue", + }, + { + id: "conference-packages", + title: "Conference packages", + href: "/conference-packages", + }, + { + id: "co-working", + title: "Co-working", + href: "/co-working", + }, + ], + }, + ], + seeAllLinkText: "See all conference & meeting venues", + infoCard: { + scriptedTitle: "Stockholm", + title: "Meeting venues in Stockholm", + description: + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed et felis metus. Sed et felis metus.", + ctaLink: "/stockholm", + }, + }, + { + id: "offers", + title: "Offers", + href: "/offers", + }, + { + id: "restaurants", + title: "Restaurants", + href: "/restaurants", + }, + ] + return ( +
+ +
+ ) +} diff --git a/components/Header/MainMenu/mainMenu.module.css b/components/Header/MainMenu/mainMenu.module.css new file mode 100644 index 000000000..e58ac423c --- /dev/null +++ b/components/Header/MainMenu/mainMenu.module.css @@ -0,0 +1,15 @@ +.mainMenu { + background-color: var(--Base-Surface-Primary-light-Normal); + padding: var(--Spacing-x2); + border-top: 1px solid var(--Base-Border-Subtle); + border-bottom: 1px solid var(--Base-Border-Subtle); +} + +.content { + max-width: 89.5rem; + margin: 0 auto; + display: flex; + justify-content: space-between; + align-items: center; + gap: var(--Spacing-x3); +} diff --git a/components/Header/TopMenu/Button/button.module.css b/components/Header/TopMenu/Button/button.module.css new file mode 100644 index 000000000..004e01d19 --- /dev/null +++ b/components/Header/TopMenu/Button/button.module.css @@ -0,0 +1,9 @@ +.button { + background-color: transparent; + color: var(--Base-Text-High-contrast); + border-width: 0; + cursor: pointer; + display: flex; + gap: var(--Spacing-x1); + align-items: center; +} diff --git a/components/Header/TopMenu/Button/button.ts b/components/Header/TopMenu/Button/button.ts new file mode 100644 index 000000000..9aa84a33f --- /dev/null +++ b/components/Header/TopMenu/Button/button.ts @@ -0,0 +1,2 @@ +export interface ButtonProps + extends React.ButtonHTMLAttributes {} diff --git a/components/Header/TopMenu/Button/index.tsx b/components/Header/TopMenu/Button/index.tsx new file mode 100644 index 000000000..437fe2d73 --- /dev/null +++ b/components/Header/TopMenu/Button/index.tsx @@ -0,0 +1,11 @@ +import { ButtonProps } from "./button" + +import styles from "./button.module.css" + +export default function Button({ children, ...props }: ButtonProps) { + return ( + + ) +} diff --git a/components/Header/TopMenu/LanguageSwitcher/index.tsx b/components/Header/TopMenu/LanguageSwitcher/index.tsx new file mode 100644 index 000000000..f13a0153a --- /dev/null +++ b/components/Header/TopMenu/LanguageSwitcher/index.tsx @@ -0,0 +1,30 @@ +"use client" + +import { useState } from "react" + +import { ChevronDownIcon, GlobeIcon } from "@/components/Icons" + +import Button from "../Button" + +import styles from "./languageSwitcher.module.css" + +export default function LanguageSwitcher() { + const [isExpanded, setIsExpanded] = useState(false) + + function handleButtonClick() { + setIsExpanded((prev) => !prev) + } + + return ( + + ) +} diff --git a/components/Header/TopMenu/LanguageSwitcher/languageSwitcher.module.css b/components/Header/TopMenu/LanguageSwitcher/languageSwitcher.module.css new file mode 100644 index 000000000..739d7d34e --- /dev/null +++ b/components/Header/TopMenu/LanguageSwitcher/languageSwitcher.module.css @@ -0,0 +1,17 @@ +.button { + background-color: transparent; + color: var(--Base-Text-High-contrast); + border-width: 0; + cursor: pointer; + display: flex; + gap: var(--Spacing-x1); + align-items: center; +} + +.chevron { + transition: transform 0.2s; +} + +.chevron.isExpanded { + transform: rotate(180deg); +} diff --git a/components/Header/TopMenu/Search/index.tsx b/components/Header/TopMenu/Search/index.tsx new file mode 100644 index 000000000..5668d49aa --- /dev/null +++ b/components/Header/TopMenu/Search/index.tsx @@ -0,0 +1,12 @@ +import { SearchIcon } from "@/components/Icons" + +import Button from "../Button" + +export default function Search() { + return ( + + ) +} diff --git a/components/Header/TopMenu/Search/search.module.css b/components/Header/TopMenu/Search/search.module.css new file mode 100644 index 000000000..004e01d19 --- /dev/null +++ b/components/Header/TopMenu/Search/search.module.css @@ -0,0 +1,9 @@ +.button { + background-color: transparent; + color: var(--Base-Text-High-contrast); + border-width: 0; + cursor: pointer; + display: flex; + gap: var(--Spacing-x1); + align-items: center; +} diff --git a/components/Header/TopMenu/index.tsx b/components/Header/TopMenu/index.tsx new file mode 100644 index 000000000..73261bc5c --- /dev/null +++ b/components/Header/TopMenu/index.tsx @@ -0,0 +1,24 @@ +import { GiftIcon } from "@/components/Icons" +import Link from "@/components/TempDesignSystem/Link" + +import LanguageSwitcher from "./LanguageSwitcher" +import Search from "./Search" + +import styles from "./topMenu.module.css" + +export default async function TopMenu() { + return ( +
+
+ + + Join Scandic Friends + +
+ + +
+
+
+ ) +} diff --git a/components/Header/TopMenu/topMenu.module.css b/components/Header/TopMenu/topMenu.module.css new file mode 100644 index 000000000..4d86f55d2 --- /dev/null +++ b/components/Header/TopMenu/topMenu.module.css @@ -0,0 +1,18 @@ +.topMenu { + background-color: var(--Base-Surface-Subtle-Normal); + padding: var(--Spacing-x2) var(--Spacing-x-one-and-half); +} + +.content { + max-width: 89.5rem; + margin: 0 auto; + display: flex; + justify-content: space-between; + gap: var(--Spacing-x3); +} + +.right { + display: flex; + gap: var(--Spacing-x2); + align-items: center; +} diff --git a/components/Header/header.module.css b/components/Header/header.module.css new file mode 100644 index 000000000..a3d728549 --- /dev/null +++ b/components/Header/header.module.css @@ -0,0 +1,4 @@ +.header { + font-family: var(--typography-Body-Regular-fontFamily); + color: var(--Base-Text-High-contrast); +} diff --git a/components/Header/index.tsx b/components/Header/index.tsx new file mode 100644 index 000000000..d6693a52b --- /dev/null +++ b/components/Header/index.tsx @@ -0,0 +1,13 @@ +import MainMenu from "./MainMenu" +import TopMenu from "./TopMenu" + +import styles from "./header.module.css" + +export default async function Header() { + return ( +
+ + +
+ ) +} diff --git a/components/Icons/Gift.tsx b/components/Icons/Gift.tsx new file mode 100644 index 000000000..b07015db5 --- /dev/null +++ b/components/Icons/Gift.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function GiftIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Person.tsx b/components/Icons/Person.tsx index 20c66c236..bc2452ac4 100644 --- a/components/Icons/Person.tsx +++ b/components/Icons/Person.tsx @@ -7,28 +7,28 @@ export default function PersonIcon({ className, color, ...props }: IconProps) { return ( - + - + diff --git a/components/Icons/Search.tsx b/components/Icons/Search.tsx new file mode 100644 index 000000000..3e91f1f5c --- /dev/null +++ b/components/Icons/Search.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function GiftIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/get-icon-by-icon-name.ts b/components/Icons/get-icon-by-icon-name.ts index e10495d52..3c7a37d7e 100644 --- a/components/Icons/get-icon-by-icon-name.ts +++ b/components/Icons/get-icon-by-icon-name.ts @@ -25,6 +25,7 @@ import { ElectricBikeIcon, EmailIcon, FitnessIcon, + GiftIcon, GlobeIcon, HouseIcon, ImageIcon, @@ -39,6 +40,7 @@ import { PlusCircleIcon, RestaurantIcon, SaunaIcon, + SearchIcon, TshirtWashIcon, WarningTriangle, WifiIcon, @@ -92,6 +94,8 @@ export function getIconByIconName(icon?: IconName): FC | null { return FacebookIcon case IconName.Fitness: return FitnessIcon + case IconName.Gift: + return GiftIcon case IconName.Globe: return GlobeIcon case IconName.House: @@ -122,6 +126,8 @@ export function getIconByIconName(icon?: IconName): FC | null { return RestaurantIcon case IconName.Sauna: return SaunaIcon + case IconName.Search: + return SearchIcon case IconName.Tripadvisor: return TripAdvisorIcon case IconName.TshirtWash: diff --git a/components/Icons/index.tsx b/components/Icons/index.tsx index 7f0259352..0dbb3b628 100644 --- a/components/Icons/index.tsx +++ b/components/Icons/index.tsx @@ -21,6 +21,7 @@ export { default as DoorOpenIcon } from "./DoorOpen" export { default as ElectricBikeIcon } from "./ElectricBike" export { default as EmailIcon } from "./Email" export { default as FitnessIcon } from "./Fitness" +export { default as GiftIcon } from "./Gift" export { default as GlobeIcon } from "./Globe" export { default as HouseIcon } from "./House" export { default as ImageIcon } from "./Image" @@ -37,6 +38,7 @@ export { default as PriceTagIcon } from "./PriceTag" export { default as RestaurantIcon } from "./Restaurant" export { default as SaunaIcon } from "./Sauna" export { default as ScandicLogoIcon } from "./ScandicLogo" +export { default as SearchIcon } from "./Search" export { default as TshirtWashIcon } from "./TshirtWash" export { default as WarningTriangle } from "./WarningTriangle" export { default as WifiIcon } from "./Wifi" diff --git a/types/components/icon.ts b/types/components/icon.ts index d965a4e78..ed0d3da3b 100644 --- a/types/components/icon.ts +++ b/types/components/icon.ts @@ -29,6 +29,7 @@ export enum IconName { Email = "Email", Facebook = "Facebook", Fitness = "Fitness", + Gift = "Gift", Globe = "Globe", House = "House", Image = "Image", @@ -44,6 +45,7 @@ export enum IconName { PlusCircle = "PlusCircle", Restaurant = "Restaurant", Sauna = "Sauna", + Search = "Search", Tripadvisor = "Tripadvisor", TshirtWash = "TshirtWash", Wifi = "Wifi",