feat(SW-1842): Making the language switcher links render in the initial HTML for SEO purposes, should also fix SW-1991 and SW-1742.

Approved-by: Matilda Landström
This commit is contained in:
Erik Tiekstra
2025-03-24 14:12:48 +00:00
parent f633ad7fcc
commit 34bc877092
6 changed files with 83 additions and 70 deletions

View File

@@ -1,6 +1,6 @@
"use client" "use client"
import { FocusTrap } from "focus-trap-react" import FocusLock from "react-focus-lock"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { logout } from "@/constants/routes/handleAuth" import { logout } from "@/constants/routes/handleAuth"
@@ -42,7 +42,7 @@ export default function MyPagesMenuContent({
} }
return ( return (
<FocusTrap focusTrapOptions={{ clickOutsideDeactivates: true }}> <FocusLock returnFocus={true}>
<nav className={styles.myPagesMenuContent}> <nav className={styles.myPagesMenuContent}>
<div className={introClassName}> <div className={introClassName}>
<Subtitle type="two" className={styles.userName}> <Subtitle type="two" className={styles.userName}>
@@ -75,7 +75,7 @@ export default function MyPagesMenuContent({
</li> </li>
</ul> </ul>
</nav> </nav>
</FocusTrap> </FocusLock>
) )
} }

View File

@@ -1,6 +1,6 @@
"use client" "use client"
import { FocusTrap } from "focus-trap-react" import FocusLock from "react-focus-lock"
import useDropdownStore from "@/stores/main-menu" import useDropdownStore from "@/stores/main-menu"
@@ -34,10 +34,7 @@ export default function MegaMenu({
} }
return ( return (
<FocusTrap <FocusLock disabled={!isOpen} returnFocus={true}>
active={isOpen}
focusTrapOptions={{ clickOutsideDeactivates: true }}
>
<nav className={`${styles.megaMenu} ${isOpen ? styles.active : ""}`}> <nav className={`${styles.megaMenu} ${isOpen ? styles.active : ""}`}>
{isMobile ? ( {isMobile ? (
<div className={styles.backWrapper}> <div className={styles.backWrapper}>
@@ -122,6 +119,6 @@ export default function MegaMenu({
) : null} ) : null}
</div> </div>
</nav> </nav>
</FocusTrap> </FocusLock>
) )
} }

View File

@@ -1,6 +1,5 @@
"use client" "use client"
import { FocusTrap } from "focus-trap-react"
import { usePathname } from "next/navigation" import { usePathname } from "next/navigation"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
@@ -29,33 +28,31 @@ export default function LanguageSwitcherContent({
const pathname = usePathname() const pathname = usePathname()
return ( return (
<FocusTrap focusTrapOptions={{ clickOutsideDeactivates: true }}> <div className={styles.languageWrapper}>
<div className={styles.languageWrapper}> <Subtitle className={styles.subtitle} type="two">
<Subtitle className={styles.subtitle} type="two"> {intl.formatMessage({ id: "Select your language" })}
{intl.formatMessage({ id: "Select your language" })} </Subtitle>
</Subtitle> <ul className={styles.list}>
<ul className={styles.list}> {urlKeys.map((key) => {
{urlKeys.map((key) => { const url = urls[key]?.url
const url = urls[key]?.url const isActive = currentLanguage === key
const isActive = currentLanguage === key if (url) {
if (url) { return (
return ( <li key={key}>
<li key={key}> <Link
<Link className={`${styles.link} ${isActive ? styles.active : ""}`}
className={`${styles.link} ${isActive ? styles.active : ""}`} href={replaceUrlPart(pathname, url)}
href={replaceUrlPart(pathname, url)} onClick={onLanguageSwitch}
onClick={onLanguageSwitch} keepSearchParams
keepSearchParams >
> {languages[key]}
{languages[key]} {isActive ? <CheckIcon color="burgundy" /> : null}
{isActive ? <CheckIcon color="burgundy" /> : null} </Link>
</Link> </li>
</li> )
) }
} })}
})} </ul>
</ul> </div>
</div>
</FocusTrap>
) )
} }

View File

@@ -2,6 +2,7 @@
import { usePathname } from "next/navigation" import { usePathname } from "next/navigation"
import { useRef } from "react" import { useRef } from "react"
import FocusLock from "react-focus-lock"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { languages } from "@/constants/languages" import { languages } from "@/constants/languages"
@@ -118,14 +119,14 @@ export default function LanguageSwitcher({ type }: LanguageSwitcherProps) {
<div <div
className={`${styles.dropdown} ${isLanguageSwitcherOpen ? styles.isExpanded : ""}`} className={`${styles.dropdown} ${isLanguageSwitcherOpen ? styles.isExpanded : ""}`}
> >
{isLanguageSwitcherOpen ? ( <FocusLock returnFocus={true} disabled={!isLanguageSwitcherOpen}>
<LanguageSwitcherContainer type={type}> <LanguageSwitcherContainer type={type}>
<LanguageSwitcherContent <LanguageSwitcherContent
urls={languagesResponse.urls} urls={languagesResponse.urls}
onLanguageSwitch={() => toggleDropdown(dropdownType)} onLanguageSwitch={() => toggleDropdown(dropdownType)}
/> />
</LanguageSwitcherContainer> </LanguageSwitcherContainer>
) : null} </FocusLock>
</div> </div>
</div> </div>
) )

View File

@@ -68,7 +68,6 @@
"embla-carousel-react": "^8.5.2", "embla-carousel-react": "^8.5.2",
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"fetch-retry": "^6.0.0", "fetch-retry": "^6.0.0",
"focus-trap-react": "^11.0.3",
"framer-motion": "^11.3.28", "framer-motion": "^11.3.28",
"graphql": "^16.8.1", "graphql": "^16.8.1",
"graphql-request": "^6.1.0", "graphql-request": "^6.1.0",
@@ -88,6 +87,7 @@
"react-day-picker": "^9.0.8", "react-day-picker": "^9.0.8",
"react-dom": "^18", "react-dom": "^18",
"react-feather": "^2.0.10", "react-feather": "^2.0.10",
"react-focus-lock": "^2.13.6",
"react-hook-form": "^7.51.2", "react-hook-form": "^7.51.2",
"react-international-phone": "^4.2.6", "react-international-phone": "^4.2.6",
"react-intl": "^6.6.8", "react-intl": "^6.6.8",

View File

@@ -1597,6 +1597,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.12.13":
version: 7.26.10
resolution: "@babel/runtime@npm:7.26.10"
dependencies:
regenerator-runtime: "npm:^0.14.0"
checksum: 10c0/6dc6d88c7908f505c4f7770fb4677dfa61f68f659b943c2be1f2a99cb6680343462867abf2d49822adc435932919b36c77ac60125793e719ea8745f2073d3745
languageName: node
linkType: hard
"@babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.24.5, @babel/runtime@npm:^7.8.4": "@babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.24.5, @babel/runtime@npm:^7.8.4":
version: 7.26.9 version: 7.26.9
resolution: "@babel/runtime@npm:7.26.9" resolution: "@babel/runtime@npm:7.26.9"
@@ -6282,7 +6291,6 @@ __metadata:
eslint-plugin-simple-import-sort: "npm:^12.1.0" eslint-plugin-simple-import-sort: "npm:^12.1.0"
fast-deep-equal: "npm:^3.1.3" fast-deep-equal: "npm:^3.1.3"
fetch-retry: "npm:^6.0.0" fetch-retry: "npm:^6.0.0"
focus-trap-react: "npm:^11.0.3"
framer-motion: "npm:^11.3.28" framer-motion: "npm:^11.3.28"
graphql: "npm:^16.8.1" graphql: "npm:^16.8.1"
graphql-request: "npm:^6.1.0" graphql-request: "npm:^6.1.0"
@@ -6309,6 +6317,7 @@ __metadata:
react-day-picker: "npm:^9.0.8" react-day-picker: "npm:^9.0.8"
react-dom: "npm:^18" react-dom: "npm:^18"
react-feather: "npm:^2.0.10" react-feather: "npm:^2.0.10"
react-focus-lock: "npm:^2.13.6"
react-hook-form: "npm:^7.51.2" react-hook-form: "npm:^7.51.2"
react-international-phone: "npm:^4.2.6" react-international-phone: "npm:^4.2.6"
react-intl: "npm:^6.6.8" react-intl: "npm:^6.6.8"
@@ -12497,27 +12506,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"focus-trap-react@npm:^11.0.3": "focus-lock@npm:^1.3.6":
version: 11.0.3 version: 1.3.6
resolution: "focus-trap-react@npm:11.0.3" resolution: "focus-lock@npm:1.3.6"
dependencies: dependencies:
focus-trap: "npm:^7.6.4" tslib: "npm:^2.0.3"
tabbable: "npm:^6.2.0" checksum: 10c0/9fc9aee762d9fc41a339cbee328b6fa9d8fdaea545aac10c7ce77358ba7800c47918a583613174d059dedc909d7a6ddd550895caf3449ca995bd66644368ec8f
peerDependencies:
"@types/react": ^18.0.0 || ^19.0.0
"@types/react-dom": ^18.0.0 || ^19.0.0
react: ^18.0.0 || ^19.0.0
react-dom: ^18.0.0 || ^19.0.0
checksum: 10c0/5a4829720901e32ef8cb0783a91e06262e079ab4a0537ffa21d866b1b70d6d383a4ee14baa19e84d952a304b4b08814a1c1fbdb2307d79a2995977735376ae45
languageName: node
linkType: hard
"focus-trap@npm:^7.6.4":
version: 7.6.4
resolution: "focus-trap@npm:7.6.4"
dependencies:
tabbable: "npm:^6.2.0"
checksum: 10c0/ed810d47fd904a5e0269e822d98e634c6cbdd7222046c712ef299bdd26a422db754e3cec04e6517065b12be4b47f65c21f6244e0c07a308b1060985463d518cb
languageName: node languageName: node
linkType: hard linkType: hard
@@ -17723,7 +17717,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": "prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1":
version: 15.8.1 version: 15.8.1
resolution: "prop-types@npm:15.8.1" resolution: "prop-types@npm:15.8.1"
dependencies: dependencies:
@@ -18024,6 +18018,17 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"react-clientside-effect@npm:^1.2.7":
version: 1.2.7
resolution: "react-clientside-effect@npm:1.2.7"
dependencies:
"@babel/runtime": "npm:^7.12.13"
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
checksum: 10c0/8d50ef578daa6bfbd970c85a22bf3179a8b237dfad2abffd866241c3fea9d0e1996bc016dca9fe616da4bfe39484836901a70230ac924671ed1fb8ee6ec40e71
languageName: node
linkType: hard
"react-day-picker@npm:^9.0.8": "react-day-picker@npm:^9.0.8":
version: 9.5.1 version: 9.5.1
resolution: "react-day-picker@npm:9.5.1" resolution: "react-day-picker@npm:9.5.1"
@@ -18098,6 +18103,26 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"react-focus-lock@npm:^2.13.6":
version: 2.13.6
resolution: "react-focus-lock@npm:2.13.6"
dependencies:
"@babel/runtime": "npm:^7.0.0"
focus-lock: "npm:^1.3.6"
prop-types: "npm:^15.6.2"
react-clientside-effect: "npm:^1.2.7"
use-callback-ref: "npm:^1.3.3"
use-sidecar: "npm:^1.1.3"
peerDependencies:
"@types/react": "*"
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
peerDependenciesMeta:
"@types/react":
optional: true
checksum: 10c0/5a3e92fb0025042ab613c54b8ff0aa6c3a45a4d4785c51c024758b02ff83a89295b458ba609c48d11f1c6bbcab6a6b032c5c17d1f3d168a2835cbbec3dea6a46
languageName: node
linkType: hard
"react-hook-form@npm:^7.51.2": "react-hook-form@npm:^7.51.2":
version: 7.54.2 version: 7.54.2
resolution: "react-hook-form@npm:7.54.2" resolution: "react-hook-form@npm:7.54.2"
@@ -20112,13 +20137,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"tabbable@npm:^6.2.0":
version: 6.2.0
resolution: "tabbable@npm:6.2.0"
checksum: 10c0/ced8b38f05f2de62cd46836d77c2646c42b8c9713f5bd265daf0e78ff5ac73d3ba48a7ca45f348bafeef29b23da7187c72250742d37627883ef89cbd7fa76898
languageName: node
linkType: hard
"table-layout@npm:^1.0.2, table-layout@npm:~1.0.0": "table-layout@npm:^1.0.2, table-layout@npm:~1.0.0":
version: 1.0.2 version: 1.0.2
resolution: "table-layout@npm:1.0.2" resolution: "table-layout@npm:1.0.2"