fix(SW-2754): Fixed issue where server rendered html included faulty links
Approved-by: Matilda Landström
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import Link from "next/link"
|
||||
import { usePathname } from "next/navigation"
|
||||
import { type ComponentProps, type PropsWithChildren, useMemo } from "react"
|
||||
import { type ComponentProps, type PropsWithChildren } from "react"
|
||||
|
||||
import { trackClick } from "@/utils/tracking"
|
||||
|
||||
@@ -16,7 +15,6 @@ export interface ButtonLinkProps
|
||||
VariantProps<typeof variants> {
|
||||
trackingId?: string
|
||||
trackingParams?: Record<string, string>
|
||||
appendToCurrentPath?: boolean
|
||||
}
|
||||
|
||||
export default function ButtonLink({
|
||||
@@ -31,10 +29,8 @@ export default function ButtonLink({
|
||||
onClick = () => {},
|
||||
trackingId,
|
||||
trackingParams,
|
||||
appendToCurrentPath,
|
||||
...props
|
||||
}: ButtonLinkProps) {
|
||||
const currentPageSlug = usePathname()
|
||||
const classNames = variants({
|
||||
variant,
|
||||
color,
|
||||
@@ -44,19 +40,10 @@ export default function ButtonLink({
|
||||
className,
|
||||
})
|
||||
|
||||
const fullUrl = useMemo(() => {
|
||||
let newPath = href
|
||||
if (appendToCurrentPath) {
|
||||
newPath = `${currentPageSlug}${newPath}`
|
||||
}
|
||||
|
||||
return newPath
|
||||
}, [href, appendToCurrentPath, currentPageSlug])
|
||||
|
||||
return (
|
||||
<Link
|
||||
className={classNames}
|
||||
href={fullUrl}
|
||||
href={href}
|
||||
target={target}
|
||||
onClick={(e) => {
|
||||
onClick(e)
|
||||
|
||||
@@ -6,6 +6,7 @@ import AdditionalAmenities from "@/components/SidePeeks/AmenitiesSidepeekContent
|
||||
import Accordion from "@/components/TempDesignSystem/Accordion"
|
||||
import SidePeek from "@/components/TempDesignSystem/SidePeek"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { appendSlugToPathname } from "@/utils/appendSlugToPathname"
|
||||
|
||||
import { SidepeekSlugs } from "@/types/components/hotelPage/hotelPage"
|
||||
import type { AmenitiesSidePeekProps } from "@/types/components/hotelPage/sidepeek/amenities"
|
||||
@@ -20,6 +21,11 @@ export default async function AmenitiesSidePeek({
|
||||
}: AmenitiesSidePeekProps) {
|
||||
const intl = await getIntl()
|
||||
|
||||
const parkingPageHref = appendSlugToPathname(parking.parkingPageUrl)
|
||||
const accessibilityPageHref = appendSlugToPathname(
|
||||
accessibility.accessibilityPageUrl
|
||||
)
|
||||
|
||||
return (
|
||||
<SidePeek
|
||||
contentKey={SidepeekSlugs.amenities}
|
||||
@@ -31,7 +37,7 @@ export default async function AmenitiesSidePeek({
|
||||
<ParkingAccordionItem
|
||||
parking={parking.parking}
|
||||
elevatorPitch={parking.parkingElevatorPitch}
|
||||
parkingPageUrl={parking.parkingPageUrl}
|
||||
parkingPageHref={parkingPageHref}
|
||||
/>
|
||||
<BreakfastAccordionItem
|
||||
restaurants={restaurants}
|
||||
@@ -39,7 +45,7 @@ export default async function AmenitiesSidePeek({
|
||||
/>
|
||||
<CheckInCheckOutAccordionItem checkInData={checkInInformation} />
|
||||
<AccessibilityAccordionItem
|
||||
accessibilityPageUrl={accessibility.accessibilityPageUrl}
|
||||
accessibilityPageHref={accessibilityPageHref}
|
||||
elevatorPitch={accessibility.elevatorPitch}
|
||||
/>
|
||||
<AdditionalAmenities amenities={amenitiesList} />
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import ButtonLink from "@/components/ButtonLink"
|
||||
import SidePeek from "@/components/TempDesignSystem/SidePeek"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { appendSlugToPathname } from "@/utils/appendSlugToPathname"
|
||||
|
||||
import SidePeekImages from "../Images"
|
||||
import { getConferenceRoomTexts } from "./util"
|
||||
@@ -23,6 +23,7 @@ export default async function MeetingsAndConferencesSidePeek({
|
||||
const intl = await getIntl()
|
||||
const { seatingText, roomText } = await getConferenceRoomTexts(meetingRooms)
|
||||
const visibleImages = meetingFacilities?.heroImages.slice(0, 2)
|
||||
const meetingPageHref = appendSlugToPathname(meetingPageUrl)
|
||||
|
||||
return (
|
||||
<SidePeek
|
||||
@@ -53,22 +54,21 @@ export default async function MeetingsAndConferencesSidePeek({
|
||||
</Body>
|
||||
) : null}
|
||||
|
||||
{meetingPageUrl && (
|
||||
{meetingPageHref ? (
|
||||
<div className={styles.buttonContainer}>
|
||||
<Button fullWidth theme="base" intent="secondary" asChild>
|
||||
<Link
|
||||
href={`/${meetingPageUrl}`}
|
||||
weight="bold"
|
||||
color="burgundy"
|
||||
appendToCurrentPath
|
||||
>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Read more",
|
||||
})}
|
||||
</Link>
|
||||
</Button>
|
||||
<ButtonLink
|
||||
variant="Secondary"
|
||||
color="Primary"
|
||||
size="Medium"
|
||||
href={meetingPageHref}
|
||||
typography="Body/Paragraph/mdBold"
|
||||
>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Read more",
|
||||
})}
|
||||
</ButtonLink>
|
||||
</div>
|
||||
)}
|
||||
) : null}
|
||||
</div>
|
||||
</SidePeek>
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ import ButtonLink from "@/components/ButtonLink"
|
||||
import OpeningHours from "@/components/OpeningHours"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { appendSlugToPathname } from "@/utils/appendSlugToPathname"
|
||||
|
||||
import SidePeekImages from "../../Images"
|
||||
|
||||
@@ -27,6 +28,10 @@ export default async function RestaurantBarItem({
|
||||
} = restaurant
|
||||
const visibleImages = restaurant.content.images.slice(0, 2)
|
||||
|
||||
const restaurantPageHref = restaurantPage
|
||||
? appendSlugToPathname(restaurant.nameInUrl)
|
||||
: null
|
||||
|
||||
return (
|
||||
<div className={styles.restaurantBarItem}>
|
||||
<div className={styles.stickyHeading}>
|
||||
@@ -87,8 +92,8 @@ export default async function RestaurantBarItem({
|
||||
</ul>
|
||||
</div>
|
||||
) : null}
|
||||
{/* If (restaurantPage && bookTableUrl && mainBody==empty), link to external restaurant page. */}
|
||||
{bookTableUrl || restaurantPage ? (
|
||||
{/* If (restaurantPageHref && bookTableUrl && mainBody==empty), link to external restaurant page. */}
|
||||
{bookTableUrl || restaurantPageHref ? (
|
||||
<div className={styles.ctaWrapper}>
|
||||
{bookTableUrl ? (
|
||||
<ButtonLink
|
||||
@@ -101,7 +106,7 @@ export default async function RestaurantBarItem({
|
||||
trackingId="book a table"
|
||||
trackingParams={{ restaurantName: name }}
|
||||
>
|
||||
{restaurantPage && !mainBody?.length
|
||||
{restaurantPageHref && !mainBody?.length
|
||||
? intl.formatMessage({
|
||||
defaultMessage: "Read more",
|
||||
})
|
||||
@@ -110,14 +115,13 @@ export default async function RestaurantBarItem({
|
||||
})}
|
||||
</ButtonLink>
|
||||
) : null}
|
||||
{restaurantPage && mainBody?.length ? (
|
||||
{restaurantPageHref && mainBody?.length ? (
|
||||
<ButtonLink
|
||||
variant="Secondary"
|
||||
color="Primary"
|
||||
size="Medium"
|
||||
typography="Body/Paragraph/mdBold"
|
||||
href={`/${restaurant.nameInUrl}`}
|
||||
appendToCurrentPath
|
||||
href={restaurantPageHref}
|
||||
>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import ButtonLink from "@/components/ButtonLink"
|
||||
import SidePeek from "@/components/TempDesignSystem/SidePeek"
|
||||
import { getIntl } from "@/i18n"
|
||||
import { appendSlugToPathname } from "@/utils/appendSlugToPathname"
|
||||
|
||||
import Facility from "./Facility"
|
||||
|
||||
@@ -15,6 +16,7 @@ export default async function WellnessAndExerciseSidePeek({
|
||||
spaPage,
|
||||
}: WellnessAndExerciseSidePeekProps) {
|
||||
const intl = await getIntl()
|
||||
const wellnessExercisePageHref = appendSlugToPathname(wellnessExercisePageUrl)
|
||||
|
||||
return (
|
||||
<SidePeek
|
||||
@@ -40,19 +42,18 @@ export default async function WellnessAndExerciseSidePeek({
|
||||
{spaPage.buttonCTA}
|
||||
</ButtonLink>
|
||||
)}
|
||||
{wellnessExercisePageUrl && (
|
||||
{wellnessExercisePageHref ? (
|
||||
<ButtonLink
|
||||
href={`/${wellnessExercisePageUrl}`}
|
||||
href={wellnessExercisePageHref}
|
||||
color="Primary"
|
||||
variant="Secondary"
|
||||
typography="Body/Paragraph/mdBold"
|
||||
appendToCurrentPath
|
||||
>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Show Gym & Wellness",
|
||||
})}
|
||||
</ButtonLink>
|
||||
)}
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</SidePeek>
|
||||
|
||||
@@ -14,11 +14,11 @@ import type { AccessibilityAccordionItemProps } from "@/types/components/sidePee
|
||||
|
||||
export default function AccessibilityAccordionItem({
|
||||
elevatorPitch,
|
||||
accessibilityPageUrl,
|
||||
accessibilityPageHref,
|
||||
}: AccessibilityAccordionItemProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
if (!elevatorPitch && !accessibilityPageUrl) {
|
||||
if (!elevatorPitch && !accessibilityPageHref) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -36,18 +36,17 @@ export default function AccessibilityAccordionItem({
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p>{elevatorPitch}</p>
|
||||
</Typography>
|
||||
{accessibilityPageUrl && (
|
||||
{accessibilityPageHref ? (
|
||||
<ButtonLink
|
||||
href={`/${accessibilityPageUrl}`}
|
||||
href={accessibilityPageHref}
|
||||
variant="Secondary"
|
||||
color="Primary"
|
||||
size="Medium"
|
||||
typography="Body/Paragraph/mdBold"
|
||||
appendToCurrentPath
|
||||
>
|
||||
{intl.formatMessage({ defaultMessage: "About accessibility" })}
|
||||
</ButtonLink>
|
||||
)}
|
||||
) : null}
|
||||
</div>
|
||||
</AccordionItem>
|
||||
)
|
||||
|
||||
@@ -16,7 +16,7 @@ import type { ParkingAccordionItemProps } from "@/types/components/sidePeeks/ame
|
||||
export default function ParkingAccordionItem({
|
||||
parking,
|
||||
elevatorPitch,
|
||||
parkingPageUrl,
|
||||
parkingPageHref,
|
||||
}: ParkingAccordionItemProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
@@ -39,20 +39,19 @@ export default function ParkingAccordionItem({
|
||||
{parking.map((data) => (
|
||||
<ParkingInformation key={data.type} parking={data} />
|
||||
))}
|
||||
{parkingPageUrl && (
|
||||
{parkingPageHref ? (
|
||||
<ButtonLink
|
||||
href={`/${parkingPageUrl}`}
|
||||
href={parkingPageHref}
|
||||
variant="Secondary"
|
||||
color="Primary"
|
||||
size="Medium"
|
||||
typography="Body/Paragraph/mdBold"
|
||||
appendToCurrentPath
|
||||
>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "About parking",
|
||||
})}
|
||||
</ButtonLink>
|
||||
)}
|
||||
) : null}
|
||||
</div>
|
||||
</AccordionItem>
|
||||
)
|
||||
|
||||
@@ -32,7 +32,6 @@ export default function Link({
|
||||
* in your component that passes the href here.
|
||||
*/
|
||||
keepSearchParams,
|
||||
appendToCurrentPath,
|
||||
...props
|
||||
}: LinkProps) {
|
||||
const currentPageSlug = usePathname()
|
||||
@@ -55,9 +54,6 @@ export default function Link({
|
||||
|
||||
const fullUrl = useMemo(() => {
|
||||
let newPath = href
|
||||
if (appendToCurrentPath) {
|
||||
newPath = `${currentPageSlug}${newPath}`
|
||||
}
|
||||
|
||||
if (keepSearchParams && searchParams.size) {
|
||||
if (newPath.includes("?")) {
|
||||
@@ -74,13 +70,7 @@ export default function Link({
|
||||
}
|
||||
|
||||
return newPath
|
||||
}, [
|
||||
href,
|
||||
searchParams,
|
||||
keepSearchParams,
|
||||
appendToCurrentPath,
|
||||
currentPageSlug,
|
||||
])
|
||||
}, [href, searchParams, keepSearchParams])
|
||||
|
||||
// TODO: Remove this check (and hook) and only return <Link /> when current web is deleted
|
||||
const isExternal = useCheckIfExternalLink(href)
|
||||
|
||||
@@ -12,5 +12,4 @@ export interface LinkProps
|
||||
trackingId?: string
|
||||
trackingParams?: Record<string, string>
|
||||
keepSearchParams?: boolean
|
||||
appendToCurrentPath?: boolean
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import type { ButtonPropsSlot } from "@/components/TempDesignSystem/Button/button"
|
||||
|
||||
export type ButtonLinkProps = React.PropsWithChildren &
|
||||
Omit<ButtonPropsSlot, "asChild"> &
|
||||
Pick<React.AnchorHTMLAttributes<HTMLAnchorElement>, "onClick" | "target"> & {
|
||||
href: string
|
||||
trackingId?: string
|
||||
trackingParams?: Record<string, string>
|
||||
appendToCurrentPath?: boolean
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import type {
|
||||
} from "@/types/hotel"
|
||||
|
||||
export interface ParkingAccordionItemProps {
|
||||
parkingPageUrl?: string
|
||||
parkingPageHref?: string | null
|
||||
parking: Parking[]
|
||||
elevatorPitch?: string
|
||||
}
|
||||
@@ -22,7 +22,7 @@ export interface CheckInCheckOutAccordionItemProps {
|
||||
|
||||
export interface AccessibilityAccordionItemProps {
|
||||
elevatorPitch?: string
|
||||
accessibilityPageUrl?: string
|
||||
accessibilityPageHref?: string | null
|
||||
}
|
||||
|
||||
export interface AdditionalAmenitiesProps {
|
||||
|
||||
14
apps/scandic-web/utils/appendSlugToPathname.ts
Normal file
14
apps/scandic-web/utils/appendSlugToPathname.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { headers } from "next/headers"
|
||||
|
||||
import { getLang } from "@/i18n/serverContext"
|
||||
|
||||
export function appendSlugToPathname(slug?: string) {
|
||||
const pathname = headers().get("x-pathname")
|
||||
const lang = getLang()
|
||||
|
||||
if (!pathname || !slug) {
|
||||
return null
|
||||
}
|
||||
|
||||
return `/${lang}${pathname}/${slug}`
|
||||
}
|
||||
Reference in New Issue
Block a user