From 915b384f1c21534ac455c26b66bd8108dc1329ab Mon Sep 17 00:00:00 2001 From: Mathias Oterhals Myklebust Date: Wed, 16 Oct 2024 12:47:39 +0200 Subject: [PATCH] feat(i18): replace useLanguage hook with server-side page data translations --- src/app/(main)/[lang]/[...path]/page.tsx | 115 ++++++++-------- src/app/(main)/[lang]/layout.tsx | 37 +----- src/app/(main)/[lang]/page.tsx | 45 +++++-- .../languageSwitcher/LanguageSwitcher.tsx | 44 +++---- .../navigation/header/Header.stories.tsx | 11 +- src/components/navigation/header/Header.tsx | 33 ++++- .../navigation/header/HeaderPreview.tsx | 15 ++- .../navigation/header/PageHeader.tsx | 59 +++++++++ src/components/navigation/mockData.ts | 12 ++ src/middlewares/languageMiddleware.ts | 22 ++-- src/utils/hooks/useLanguage.ts | 123 ------------------ src/utils/pageData.ts | 62 +++++++++ studio/lib/interfaces/slugTranslations.ts | 8 -- studio/lib/queries/slugTranslations.ts | 88 +++++-------- 14 files changed, 340 insertions(+), 334 deletions(-) create mode 100644 src/components/navigation/header/PageHeader.tsx delete mode 100644 src/utils/hooks/useLanguage.ts delete mode 100644 studio/lib/interfaces/slugTranslations.ts diff --git a/src/app/(main)/[lang]/[...path]/page.tsx b/src/app/(main)/[lang]/[...path]/page.tsx index fcf222f5f..628c964e0 100644 --- a/src/app/(main)/[lang]/[...path]/page.tsx +++ b/src/app/(main)/[lang]/[...path]/page.tsx @@ -7,6 +7,7 @@ import CustomerCasesPreview from "src/components/customerCases/CustomerCasesPrev import CustomErrorMessage from "src/components/customErrorMessage/CustomErrorMessage"; import Legal from "src/components/legal/Legal"; import LegalPreview from "src/components/legal/LegalPreview"; +import PageHeader from "src/components/navigation/header/PageHeader"; import { homeLink } from "src/components/utils/linkTypes"; import { getDraftModeInfo } from "src/utils/draftmode"; import { fetchPageDataFromParams } from "src/utils/pageData"; @@ -77,60 +78,68 @@ async function Page({ params }: Props) { return Page404; } - const { queryResponse, docType } = pageData; + const { queryResponse, docType, pathTranslations } = pageData; - switch (docType) { - case "pageBuilder": - return ( - <> - {queryResponse.data?.sections?.map((section, index) => ( - - ))} - - ); - case "compensations": - return isDraftMode ? ( - - ) : ( - - ); - case "customerCasesPage": - return isDraftMode ? ( - - ) : ( - - ); - case "customerCase": - return ( - // TODO: implement customer case detail page -
-          {JSON.stringify(pageData, null, 2)}
-        
- ); - case "legalDocument": - return isDraftMode ? ( - - ) : ( - - ); - } - - return Page404; + return ( + <> + +
+ {(() => { + switch (docType) { + case "pageBuilder": + return ( + <> + {queryResponse.data?.sections?.map((section, index) => ( + + ))} + + ); + case "compensations": + return isDraftMode ? ( + + ) : ( + + ); + case "customerCasesPage": + return isDraftMode ? ( + + ) : ( + + ); + case "customerCase": + return ( + // TODO: implement customer case detail page +
+                  {JSON.stringify(pageData, null, 2)}
+                
+ ); + case "legalDocument": + return isDraftMode ? ( + + ) : ( + + ); + } + return Page404; + })()} +
+ + ); } export default Page; diff --git a/src/app/(main)/[lang]/layout.tsx b/src/app/(main)/[lang]/layout.tsx index fa07aa0cc..f0b4ad3d7 100644 --- a/src/app/(main)/[lang]/layout.tsx +++ b/src/app/(main)/[lang]/layout.tsx @@ -3,8 +3,6 @@ import { draftMode } from "next/headers"; import Footer from "src/components/navigation/footer/Footer"; import FooterPreview from "src/components/navigation/footer/FooterPreview"; -import { Header } from "src/components/navigation/header/Header"; -import HeaderPreview from "src/components/navigation/header/HeaderPreview"; import SkipToMain from "src/components/skipToMain/SkipToMain"; import { getDraftModeInfo } from "src/utils/draftmode"; import { BrandAssets } from "studio/lib/interfaces/brandAssets"; @@ -24,8 +22,6 @@ import { } from "studio/lib/queries/siteSettings"; import { loadStudioQuery } from "studio/lib/store"; -import styles from "./layout.module.css"; - import "src/styles/global.css"; const fontBrittiSans = localFont({ @@ -73,45 +69,14 @@ export default async function Layout({ ]); const hasNavData = hasValidData(initialNav.data); - const hasCompanyInfoData = hasValidData(initialCompanyInfo.data); - - const hasHeaderData = - hasNavData && (initialNav.data.main || initialNav.data.sidebar); const hasFooterData = hasNavData && initialNav.data.footer; - const hasMenuData = hasCompanyInfoData && (hasHeaderData || hasFooterData); - - if (!hasMenuData) { - return ( - - -
- {children} -
- - - ); - } return ( - {hasHeaderData && isDraftMode ? ( - - ) : ( -
- )} -
- {children} -
+ {children} {hasFooterData && isDraftMode ? ( { @@ -53,16 +59,33 @@ const Home = async ({ params }: Props) => { ); } - return initialLandingPage.data.sections.map((section, index) => ( - - )); + const languages = await loadStudioQuery( + LANGUAGES_QUERY, + ); + + const pathTranslations: InternationalizedString = + languages?.data?.map((language) => ({ + _key: language.id, + value: "", + })) ?? []; + + return ( + <> + +
+ {initialLandingPage.data.sections.map((section, index) => ( + + ))} +
+ + ); }; export default Home; diff --git a/src/components/languageSwitcher/LanguageSwitcher.tsx b/src/components/languageSwitcher/LanguageSwitcher.tsx index dfdff5485..c0ddd0654 100644 --- a/src/components/languageSwitcher/LanguageSwitcher.tsx +++ b/src/components/languageSwitcher/LanguageSwitcher.tsx @@ -2,46 +2,42 @@ import Link from "next/link"; import { Fragment } from "react"; import Text from "src/components/text/Text"; -import useLanguage from "src/utils/hooks/useLanguage"; +import { InternationalizedString } from "studio/lib/interfaces/global"; import styles from "./languageSwitcher.module.css"; -export default function LanguageSwitcher() { - const { slugTranslations, language, defaultLanguage } = useLanguage(); - - const currentLanguage = language ?? defaultLanguage; - - // make sure the current language is the first item in the languages list - const sortedTranslations = slugTranslations?.toSorted((a, b) => - a?.language?.id === currentLanguage?.id - ? -1 - : b?.language?.id === currentLanguage?.id - ? 1 - : 0, - ); +export interface LanguageSwitcherProps { + currentLanguage: string; + pathTranslations: InternationalizedString; +} +export default function LanguageSwitcher({ + currentLanguage, + pathTranslations, +}: LanguageSwitcherProps) { return (
    - {sortedTranslations?.map((slugTranslation, index) => { - if (slugTranslation?.language === undefined) { + {pathTranslations?.map((pathTranslation, index) => { + if (pathTranslation._key === undefined) { return null; } const linkText = ( - - {slugTranslation.language.id.toUpperCase()} - + {pathTranslation._key.toUpperCase()} ); return ( - +
  • - {currentLanguage === undefined || - slugTranslation.language.id !== currentLanguage.id ? ( - {linkText} + {pathTranslation._key !== currentLanguage ? ( + + {linkText} + ) : ( linkText )}
  • - {index < sortedTranslations.length - 1 && ( + {index < pathTranslations.length - 1 && ( )}
    diff --git a/src/components/navigation/header/Header.stories.tsx b/src/components/navigation/header/Header.stories.tsx index b9d2f8307..469f0ba7f 100644 --- a/src/components/navigation/header/Header.stories.tsx +++ b/src/components/navigation/header/Header.stories.tsx @@ -1,6 +1,11 @@ import { Meta, StoryObj } from "@storybook/react"; -import { mockLogo, mockNavigation } from "src/components/navigation/mockData"; +import { defaultLanguage } from "i18n/supportedLanguages"; +import { + mockLogo, + mockNavigation, + mockPathTranslations, +} from "src/components/navigation/mockData"; import { Header } from "./Header"; @@ -23,7 +28,9 @@ type Story = StoryObj; export const Default: Story = { args: { - data: mockNavigation, + navigation: mockNavigation, assets: mockLogo, + currentLanguage: defaultLanguage?.id ?? "en", + pathTranslations: mockPathTranslations, }, }; diff --git a/src/components/navigation/header/Header.tsx b/src/components/navigation/header/Header.tsx index f9522ab57..857c1a6e4 100644 --- a/src/components/navigation/header/Header.tsx +++ b/src/components/navigation/header/Header.tsx @@ -5,12 +5,14 @@ import { usePathname } from "next/navigation"; import { useEffect, useState } from "react"; import { FocusOn } from "react-focus-on"; +import { defaultLanguage } from "i18n/supportedLanguages"; import { SanityImage } from "src/components/image/SanityImage"; import LanguageSwitcher from "src/components/languageSwitcher/LanguageSwitcher"; import CustomLink from "src/components/link/CustomLink"; import LinkButton from "src/components/linkButton/LinkButton"; import { getHref } from "src/utils/link"; import { BrandAssets } from "studio/lib/interfaces/brandAssets"; +import { InternationalizedString } from "studio/lib/interfaces/global"; import { ILink, Navigation } from "studio/lib/interfaces/navigation"; import { callToActionFieldID } from "studio/schemas/fields/callToActionFields"; import { linkID } from "studio/schemas/objects/link"; @@ -18,21 +20,28 @@ import { linkID } from "studio/schemas/objects/link"; import styles from "./header.module.css"; export interface IHeader { - data: Navigation; + navigation: Navigation; assets: BrandAssets; + currentLanguage: string; + pathTranslations: InternationalizedString; } const filterLinks = (data: ILink[], type: string) => data?.filter((link) => link._type === type); -export const Header = ({ data, assets }: IHeader) => { +export const Header = ({ + navigation, + assets, + currentLanguage, + pathTranslations, +}: IHeader) => { const pathname = usePathname(); const [isOpen, setIsOpen] = useState(false); const [isScrolled, setIsScrolled] = useState(false); - const sidebarData = data.sidebar || data.main; + const sidebarData = navigation.sidebar || navigation.main; - const links = filterLinks(data.main, linkID); - const ctas = filterLinks(data.main, callToActionFieldID); + const links = filterLinks(navigation.main, linkID); + const ctas = filterLinks(navigation.main, callToActionFieldID); const sidebarLinks = filterLinks(sidebarData, linkID); const sidebarCtas = filterLinks(sidebarData, callToActionFieldID); @@ -86,7 +95,12 @@ export const Header = ({ data, assets }: IHeader) => { {renderPageLinks(links, false, pathname)} {renderPageCTAs(ctas, false)}
    - + {defaultLanguage && ( + + )}