feat(SW-187): simplify dropdown menu

This commit is contained in:
Pontus Dreij
2024-09-09 14:58:22 +02:00
parent ef33d082d8
commit 07eb0401bb
9 changed files with 93 additions and 158 deletions

View File

@@ -18,6 +18,7 @@ import LoginButton from "../LoginButton"
import styles from "./mainMenu.module.css"
import type { MainMenuProps } from "@/types/components/current/header/mainMenu"
import { DropdownType } from "@/types/components/dropdown/dropdown"
export function MainMenu({
frontpageLinkText,
@@ -61,12 +62,11 @@ export function MainMenu({
"/sv/current-content-page",
].includes(pathname)
const {
isHamburgerMenuOpen,
isMyPagesMobileMenuOpen,
toggleHamburgerMenu,
toggleMyPagesMobileMenu,
} = useDropdownStore()
const { toggleDropdown, openDropdown } = useDropdownStore()
const isMyPagesMobileMenuOpen =
openDropdown === DropdownType.MyPagesMobileMenu
const isHamburgerMenuOpen = openDropdown === DropdownType.HamburgerMenu
function handleMyPagesMobileMenuClick() {
// Only track click when opening it
@@ -74,7 +74,7 @@ export function MainMenu({
trackClick("profile picture icon")
}
toggleMyPagesMobileMenu()
toggleDropdown(DropdownType.MyPagesMobileMenu)
}
return (
@@ -89,7 +89,7 @@ export function MainMenu({
<button
aria-pressed="false"
className={`${styles.expanderBtn} ${isHamburgerMenuOpen ? styles.expanded : ""}`}
onClick={toggleHamburgerMenu}
onClick={() => toggleDropdown(DropdownType.HamburgerMenu)}
type="button"
>
<span className={styles.iconBars}></span>

View File

@@ -13,6 +13,8 @@ import useLang from "@/hooks/useLang"
import styles from "./my-pages-mobile-dropdown.module.css"
import { DropdownType } from "@/types/components/dropdown/dropdown"
type Navigation = Awaited<ReturnType<(typeof navigationQueryRouter)["get"]>>
export default function MyPagesMobileDropdown({
@@ -22,8 +24,10 @@ export default function MyPagesMobileDropdown({
}) {
const { formatMessage } = useIntl()
const lang = useLang()
const { toggleMyPagesMobileMenu, isMyPagesMobileMenuOpen } =
useDropdownStore()
const { toggleDropdown, openDropdown } = useDropdownStore()
const isMyPagesMobileMenuOpen =
openDropdown === DropdownType.MyPagesMobileMenu
if (!navigation) {
return null
@@ -51,7 +55,9 @@ export default function MyPagesMobileDropdown({
size={menuItem.display_sign_out_link ? "small" : "regular"}
variant="myPageMobileDropdown"
color="burgundy"
onClick={toggleMyPagesMobileMenu}
onClick={() =>
toggleDropdown(DropdownType.MyPagesMobileMenu)
}
>
{link.linkText}
</Link>

View File

@@ -14,6 +14,7 @@ import NavigationMenu from "../NavigationMenu"
import styles from "./mobileMenu.module.css"
import { DropdownType } from "@/types/components/dropdown/dropdown"
import type { MobileMenuProps } from "@/types/components/header/mobileMenu"
export default function MobileMenu({
@@ -22,30 +23,28 @@ export default function MobileMenu({
topLink,
}: MobileMenuProps) {
const intl = useIntl()
const {
isHamburgerMenuOpen,
isMyPagesMobileMenuOpen,
isHeaderLanguageSwitcherOpen,
toggleHamburgerMenu,
toggleMyPagesMobileMenu,
toggleLanguageSwitcher,
} = useDropdownStore()
const { toggleDropdown, openDropdown } = useDropdownStore()
const isHamburgerMenuOpen = openDropdown === DropdownType.HamburgerMenu
const isMyPagesMobileMenuOpen =
openDropdown === DropdownType.MyPagesMobileMenu
const isHeaderLanguageSwitcherOpen =
openDropdown === DropdownType.HeaderLanguage
useHandleKeyUp((event: KeyboardEvent) => {
if (event.key === "Escape" && isHamburgerMenuOpen) {
toggleHamburgerMenu()
toggleDropdown(DropdownType.HamburgerMenu)
}
})
function handleHamburgerClick() {
if (isMyPagesMobileMenuOpen) {
toggleMyPagesMobileMenu()
toggleDropdown(DropdownType.MyPagesMobileMenu)
} else {
if (isHeaderLanguageSwitcherOpen) {
toggleLanguageSwitcher("header")
toggleDropdown(DropdownType.HeaderLanguage)
}
toggleHamburgerMenu()
toggleDropdown(DropdownType.HamburgerMenu)
}
}
@@ -61,7 +60,10 @@ export default function MobileMenu({
>
<span className={styles.bar}></span>
</button>
<Modal className={styles.modal} isOpen={isHamburgerMenuOpen}>
<Modal
className={styles.modal}
isOpen={isHamburgerMenuOpen || isHeaderLanguageSwitcherOpen}
>
<Dialog
className={styles.dialog}
aria-label={intl.formatMessage({ id: "Menu" })}

View File

@@ -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 { DropdownType } from "@/types/components/dropdown/dropdown"
import type { MyPagesMenuProps } from "@/types/components/header/myPagesMenu"
export default function MyPagesMenu({
@@ -24,18 +24,22 @@ export default function MyPagesMenu({
user,
}: MyPagesMenuProps) {
const intl = useIntl()
const lang = useLang()
const { toggleMyPagesMenu, isMyPagesMenuOpen } = useDropdownStore()
const { toggleDropdown, openDropdown } = useDropdownStore()
const isMyPagesMenuOpen = openDropdown === DropdownType.MyPagesMenu
useHandleKeyUp((event: KeyboardEvent) => {
if (event.key === "Escape" && isMyPagesMenuOpen) {
toggleMyPagesMenu()
toggleDropdown(DropdownType.MyPagesMenu)
}
})
return (
<div className={styles.myPagesMenu}>
<MainMenuButton className={styles.button} onClick={toggleMyPagesMenu}>
<MainMenuButton
className={styles.button}
onClick={() => toggleDropdown(DropdownType.MyPagesMenu)}
>
<Avatar initials={getInitials(user.firstName, user.lastName)} />
<Subtitle type="two" className={styles.userName}>
{intl.formatMessage({ id: "Hi" })} {user.firstName}!
@@ -51,7 +55,7 @@ export default function MyPagesMenu({
navigation={navigation}
user={user}
membership={membership}
toggleOpenStateFn={toggleMyPagesMenu}
toggleOpenStateFn={() => toggleDropdown(DropdownType.MyPagesMenu)}
/>
</div>
) : null}

View File

@@ -14,6 +14,7 @@ import MyPagesMenuContent from "../MyPagesMenuContent"
import styles from "./myPagesMobileMenu.module.css"
import { DropdownType } from "@/types/components/dropdown/dropdown"
import type { MyPagesMenuProps } from "@/types/components/header/myPagesMenu"
export default function MyPagesMobileMenu({
@@ -22,12 +23,13 @@ export default function MyPagesMobileMenu({
user,
}: MyPagesMenuProps) {
const intl = useIntl()
const { toggleMyPagesMobileMenu, isMyPagesMobileMenuOpen } =
useDropdownStore()
const { openDropdown, toggleDropdown } = useDropdownStore()
const isMyPagesMobileMenuOpen =
openDropdown === DropdownType.MyPagesMobileMenu
useHandleKeyUp((event: KeyboardEvent) => {
if (event.key === "Escape" && isMyPagesMobileMenuOpen) {
toggleMyPagesMobileMenu()
toggleDropdown(DropdownType.MyPagesMobileMenu)
}
})
@@ -35,7 +37,7 @@ export default function MyPagesMobileMenu({
<div className={styles.myPagesMobileMenu}>
<MainMenuButton
className={styles.button}
onClick={toggleMyPagesMobileMenu}
onClick={() => toggleDropdown(DropdownType.MyPagesMobileMenu)}
aria-label={intl.formatMessage({ id: "Open my pages menu" })}
>
<Avatar initials={getInitials(user.firstName, user.lastName)} />
@@ -49,7 +51,9 @@ export default function MyPagesMobileMenu({
membership={membership}
navigation={navigation}
user={user}
toggleOpenStateFn={toggleMyPagesMobileMenu}
toggleOpenStateFn={() =>
toggleDropdown(DropdownType.MyPagesMobileMenu)
}
/>
</Dialog>
</Modal>

View File

@@ -13,6 +13,7 @@ import { useTrapFocus } from "@/hooks/useTrapFocus"
import styles from "./languageSwitcherContent.module.css"
import { DropdownType } from "@/types/components/dropdown/dropdown"
import type { LanguageSwitcherProps } from "@/types/components/languageSwitcher/languageSwitcher"
export default function LanguageSwitcherContent({
@@ -21,10 +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" ? "footer" : "header"
const position =
type === "footer"
? DropdownType.FooterLanguage
: DropdownType.HeaderLanguage
return (
<div className={styles.languageSwitcherContent} ref={languageSwitcherRef}>
@@ -33,7 +37,7 @@ export default function LanguageSwitcherContent({
<button
type="button"
className={styles.backButton}
onClick={() => toggleLanguageSwitcher(position)}
onClick={() => toggleDropdown(position)}
>
<ChevronLeftIcon color="red" />
<Subtitle type="one">Main Menu</Subtitle>

View File

@@ -14,6 +14,7 @@ import { languageSwitcherVariants } from "./variants"
import styles from "./languageSwitcher.module.css"
import { DropdownType } from "@/types/components/dropdown/dropdown"
import type { LanguageSwitcherProps } from "@/types/components/languageSwitcher/languageSwitcher"
export default function LanguageSwitcher({
@@ -22,22 +23,23 @@ export default function LanguageSwitcher({
}: LanguageSwitcherProps) {
const intl = useIntl()
const currentLanguage = useLang()
const {
toggleLanguageSwitcher,
isHeaderLanguageSwitcherOpen,
isFooterLanguageSwitcherOpen,
} = useDropdownStore()
const { toggleDropdown, openDropdown } = useDropdownStore()
const position = type === "footer" ? "footer" : "header"
const dropdownType =
type === "footer"
? DropdownType.FooterLanguage
: DropdownType.HeaderLanguage
const color = type === "footer" ? "pale" : "burgundy"
const isLanguageSwitcherOpen =
type === "footer"
? isFooterLanguageSwitcherOpen
: isHeaderLanguageSwitcherOpen
? openDropdown === DropdownType.FooterLanguage
: openDropdown === DropdownType.HeaderLanguage
useHandleKeyUp((event: KeyboardEvent) => {
if (event.key === "Escape" && isLanguageSwitcherOpen) {
toggleLanguageSwitcher(position)
toggleDropdown(dropdownType)
}
})
@@ -53,7 +55,7 @@ export default function LanguageSwitcher({
? "Close language menu"
: "Open language menu",
})}
onClick={() => toggleLanguageSwitcher(position)}
onClick={() => toggleDropdown(dropdownType)}
>
<GlobeIcon width={20} height={20} color={color} />
<span>{languages[currentLanguage]}</span>

View File

@@ -1,118 +1,16 @@
import { create } from "zustand"
// TODO: When MyPagesMobileMenu is removed, also remove the
// isMyPagesMobileMenuOpen state and toggleMyPagesMobileMenu function
interface DropdownState {
isHamburgerMenuOpen: boolean
isMyPagesMobileMenuOpen: boolean
isMyPagesMenuOpen: boolean
isHeaderLanguageSwitcherOpen: boolean
isFooterLanguageSwitcherOpen: boolean
toggleHamburgerMenu: () => void
toggleMyPagesMobileMenu: () => void
toggleMyPagesMenu: () => void
toggleLanguageSwitcher: (location: "header" | "footer") => void
}
import {
type DropdownState,
DropdownType,
} from "@/types/components/dropdown/dropdown"
const useDropdownStore = create<DropdownState>((set) => ({
isHamburgerMenuOpen: false,
isMyPagesMobileMenuOpen: false,
isMyPagesMenuOpen: false,
isHeaderLanguageSwitcherOpen: false,
isFooterLanguageSwitcherOpen: false,
toggleHamburgerMenu: () =>
set(
({ isHamburgerMenuOpen, isMyPagesMenuOpen, isMyPagesMobileMenuOpen }) => {
// Close the other dropdowns if they're open
if (!isHamburgerMenuOpen) {
if (isMyPagesMenuOpen) {
set({ isMyPagesMenuOpen: false })
}
if (isMyPagesMobileMenuOpen) {
set({ isMyPagesMobileMenuOpen: false })
}
}
return { isHamburgerMenuOpen: !isHamburgerMenuOpen }
}
),
toggleMyPagesMobileMenu: () =>
set(
({
isMyPagesMenuOpen,
isMyPagesMobileMenuOpen,
isHamburgerMenuOpen,
isHeaderLanguageSwitcherOpen,
isFooterLanguageSwitcherOpen,
}) => {
// Close the other dropdowns if they're open
if (!isMyPagesMobileMenuOpen) {
if (isMyPagesMenuOpen) {
set({ isMyPagesMenuOpen: false })
}
if (isHamburgerMenuOpen) {
set({ isHamburgerMenuOpen: false })
}
if (isHeaderLanguageSwitcherOpen) {
set({ isHeaderLanguageSwitcherOpen: false })
}
if (isFooterLanguageSwitcherOpen) {
set({ isFooterLanguageSwitcherOpen: false })
}
}
return { isMyPagesMobileMenuOpen: !isMyPagesMobileMenuOpen }
}
),
toggleMyPagesMenu: () =>
set(
({
isHamburgerMenuOpen,
isHeaderLanguageSwitcherOpen,
isFooterLanguageSwitcherOpen,
isMyPagesMenuOpen,
isMyPagesMobileMenuOpen,
}) => {
// Close the other dropdowns if they're open
if (!isMyPagesMenuOpen) {
if (isHamburgerMenuOpen) {
set({ isHamburgerMenuOpen: false })
}
if (isMyPagesMobileMenuOpen) {
set({ isMyPagesMobileMenuOpen: false })
}
if (isHeaderLanguageSwitcherOpen) {
set({ isHeaderLanguageSwitcherOpen: false })
}
if (isFooterLanguageSwitcherOpen) {
set({ isFooterLanguageSwitcherOpen: false })
}
}
return { isMyPagesMenuOpen: !isMyPagesMenuOpen }
}
),
toggleLanguageSwitcher: (location: "header" | "footer") =>
set((state) => {
const isCurrentlyOpen =
location === "header"
? state.isHeaderLanguageSwitcherOpen
: state.isFooterLanguageSwitcherOpen
if (!isCurrentlyOpen) {
return {
isHeaderLanguageSwitcherOpen: location === "header",
isFooterLanguageSwitcherOpen: location === "footer",
isMyPagesMenuOpen: false,
isMyPagesMobileMenuOpen: false,
isHamburgerMenuOpen: false,
}
}
return {
isHeaderLanguageSwitcherOpen:
location === "header" ? false : state.isHeaderLanguageSwitcherOpen,
isFooterLanguageSwitcherOpen:
location === "footer" ? false : state.isFooterLanguageSwitcherOpen,
}
}),
openDropdown: null,
toggleDropdown: (dropdown: DropdownType) =>
set((state) => ({
openDropdown: state.openDropdown === dropdown ? null : dropdown,
})),
}))
export default useDropdownStore

View File

@@ -0,0 +1,15 @@
// TODO: When MyPagesMobileMenu is removed, also remove the
// isMyPagesMobileMenuOpen state
export enum DropdownType {
HeaderLanguage,
FooterLanguage,
HamburgerMenu,
MyPagesMenu,
MyPagesMobileMenu,
}
export interface DropdownState {
openDropdown: DropdownType | null
toggleDropdown: (dropdown: DropdownType) => void
}