Merged in feat/SW-1067-special-needs-accessibility-page (pull request #1253)

feat/SW-1067 special needs accessibility page

* feat(SW-1067): update types

* feat(SW-1067): add sidebar

* feat(SW-1067): add additional content component

* feat(SW-1067): add special needs list

* feat(SW-1067): update type

* feat(SW-1067): update import

* feat(SW-1067): remove component

* feat(SW-1067): re add component

* feat(SW-1067): update folder structure


Approved-by: Erik Tiekstra
This commit is contained in:
Fredrik Thorsson
2025-02-05 10:27:26 +00:00
parent 86a7650813
commit a389fba8ce
21 changed files with 145 additions and 16 deletions

View File

@@ -1,8 +1,11 @@
import { accessibilitySubPage } from "@/constants/routes/hotelSubpages"
import { ArrowRightIcon } from "@/components/Icons" import { ArrowRightIcon } from "@/components/Icons"
import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem" import AccordionItem from "@/components/TempDesignSystem/Accordion/AccordionItem"
import Link from "@/components/TempDesignSystem/Link" import Link from "@/components/TempDesignSystem/Link"
import Body from "@/components/TempDesignSystem/Text/Body" import Body from "@/components/TempDesignSystem/Text/Body"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import { getLang } from "@/i18n/serverContext"
import styles from "./accessibilityAmenity.module.css" import styles from "./accessibilityAmenity.module.css"
@@ -10,9 +13,12 @@ import type { AccessibilityAmenityProps } from "@/types/components/hotelPage/sid
import { IconName } from "@/types/components/icon" import { IconName } from "@/types/components/icon"
export default async function AccessibilityAmenity({ export default async function AccessibilityAmenity({
accessibility, elevatorPitch,
hasExtraAccessibilityPage,
}: AccessibilityAmenityProps) { }: AccessibilityAmenityProps) {
const intl = await getIntl() const intl = await getIntl()
const lang = getLang()
return ( return (
<AccordionItem <AccordionItem
title={intl.formatMessage({ id: "Accessibility" })} title={intl.formatMessage({ id: "Accessibility" })}
@@ -21,15 +27,16 @@ export default async function AccessibilityAmenity({
trackingId="amenities:accessibility" trackingId="amenities:accessibility"
> >
<div className={styles.wrapper}> <div className={styles.wrapper}>
{accessibility?.description && ( {elevatorPitch && (
<Body color="uiTextHighContrast">{accessibility.description}</Body> <Body color="uiTextHighContrast">{elevatorPitch}</Body>
)} )}
{accessibility?.link && ( {hasExtraAccessibilityPage && (
<Link <Link
href={accessibility.link} href={`/${accessibilitySubPage[lang]}`}
color="burgundy" color="burgundy"
textDecoration="underline" textDecoration="underline"
variant="icon" variant="icon"
appendToCurrentPath
> >
{intl.formatMessage({ id: "About accessibility" })} {intl.formatMessage({ id: "About accessibility" })}
<ArrowRightIcon color="burgundy" /> <ArrowRightIcon color="burgundy" />

View File

@@ -70,8 +70,12 @@ export default async function AmenitiesSidePeek({
/> />
)} )}
<CheckInAmenity checkInInformation={checkInInformation} /> <CheckInAmenity checkInInformation={checkInInformation} />
{accessibility && ( {(accessibility.elevatorPitch ||
<AccessibilityAmenity accessibility={accessibility} /> accessibility.hasExtraAccessibilityPage) && (
<AccessibilityAmenity
hasExtraAccessibilityPage={accessibility.hasExtraAccessibilityPage}
elevatorPitch={accessibility.elevatorPitch}
/>
)} )}
<FilteredAmenities filteredAmenities={filteredAmenities} /> <FilteredAmenities filteredAmenities={filteredAmenities} />
</Accordion> </Accordion>

View File

@@ -88,6 +88,7 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
gallery, gallery,
hotelParking, hotelParking,
displayWebPage, displayWebPage,
hotelSpecialNeeds,
} = hotelData.additionalData } = hotelData.additionalData
const images = gallery?.smallerImages const images = gallery?.smallerImages
@@ -217,7 +218,10 @@ export default async function HotelPage({ hotelId }: HotelPageProps) {
hasExtraParkingPage: displayWebPage.parking, hasExtraParkingPage: displayWebPage.parking,
}} }}
checkInInformation={hotelFacts.checkin} checkInInformation={hotelFacts.checkin}
accessibility={hotelFacts.hotelInformation.accessibility} accessibility={{
elevatorPitch: hotelSpecialNeeds.elevatorPitch,
hasExtraAccessibilityPage: displayWebPage.specialNeeds,
}}
restaurants={restaurants} restaurants={restaurants}
/> />
<AboutTheHotelSidePeek <AboutTheHotelSidePeek

View File

@@ -0,0 +1,23 @@
.container {
display: grid;
gap: var(--Spacing-x2);
}
.accessibilityGroup {
display: grid;
gap: var(--Spacing-x1);
}
.list {
display: grid;
gap: var(--Spacing-x1);
list-style-type: none;
}
.list > li::before {
content: url("/_static/icons/heart.svg");
position: relative;
height: 8px;
top: 3px;
margin-right: var(--Spacing-x1);
}

View File

@@ -0,0 +1,40 @@
import Body from "@/components/TempDesignSystem/Text/Body"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import styles from "./accessibilityAdditionalContent.module.css"
import type { AdditionalData } from "@/types/hotel"
interface AccessibilityAdditionalContentProps {
additionalData: AdditionalData
}
export default function AccessibilityAdditionalContent({
additionalData,
}: AccessibilityAdditionalContentProps) {
return (
<div className={styles.container}>
{additionalData.specialNeedGroups.map((accessibilityGroup) => (
<div
key={accessibilityGroup.name}
className={styles.accessibilityGroup}
>
<Subtitle type="two" color="uiTextHighContrast" asChild>
<h2>{accessibilityGroup.name}</h2>
</Subtitle>
<ul className={styles.list}>
{accessibilityGroup.specialNeeds.map((groupItem) => (
<Body key={groupItem.name} asChild>
<li>
{groupItem.details
? `${groupItem.name}: ${groupItem.details}`
: groupItem.name}
</li>
</Body>
))}
</ul>
</div>
))}
</div>
)
}

View File

@@ -1,6 +1,6 @@
import ParkingInformation from "@/components/ParkingInformation" import ParkingInformation from "@/components/ParkingInformation"
import styles from "./additionalContent.module.css" import styles from "./parkingAdditionalContent.module.css"
import type { Hotel } from "@/types/hotel" import type { Hotel } from "@/types/hotel"

View File

@@ -1,22 +1,26 @@
import { import {
accessibilitySubPage,
parkingSubPage, parkingSubPage,
wellnessSubPage, wellnessSubPage,
} from "@/constants/routes/hotelSubpages" } from "@/constants/routes/hotelSubpages"
import { getLang } from "@/i18n/serverContext" import { getLang } from "@/i18n/serverContext"
import ParkingAdditionalContent from "./ParkingAdditionalContent" import AccessibilityAdditionalContent from "./Accessibility"
import ParkingAdditionalContent from "./Parking"
import type { Hotel } from "@/types/hotel" import type { AdditionalData, Hotel } from "@/types/hotel"
interface HotelSubpageAdditionalContentProps { interface HotelSubpageAdditionalContentProps {
subpage: string subpage: string
hotel: Hotel hotel: Hotel
additionalData: AdditionalData
} }
export default function HotelSubpageAdditionalContent({ export default function HotelSubpageAdditionalContent({
subpage, subpage,
hotel, hotel,
additionalData,
}: HotelSubpageAdditionalContentProps) { }: HotelSubpageAdditionalContentProps) {
const lang = getLang() const lang = getLang()
@@ -25,6 +29,8 @@ export default function HotelSubpageAdditionalContent({
return <ParkingAdditionalContent hotel={hotel} /> return <ParkingAdditionalContent hotel={hotel} />
case wellnessSubPage[lang]: case wellnessSubPage[lang]:
return null return null
case accessibilitySubPage[lang]:
return <AccessibilityAdditionalContent additionalData={additionalData} />
default: default:
return null return null
} }

View File

@@ -1,4 +1,5 @@
import { import {
accessibilitySubPage,
parkingSubPage, parkingSubPage,
wellnessSubPage, wellnessSubPage,
} from "@/constants/routes/hotelSubpages" } from "@/constants/routes/hotelSubpages"
@@ -25,6 +26,8 @@ export default function HotelSubpageSidebar({
return <ParkingSidebar hotel={hotel} /> return <ParkingSidebar hotel={hotel} />
case wellnessSubPage[lang]: case wellnessSubPage[lang]:
return <WellnessSidebar hotel={hotel} /> return <WellnessSidebar hotel={hotel} />
case accessibilitySubPage[lang]:
return null
default: default:
return null return null
} }

View File

@@ -63,7 +63,9 @@ export default async function HotelSubpage({
<div className={styles.contentContainer}> <div className={styles.contentContainer}>
<main className={styles.mainContent}> <main className={styles.mainContent}>
<div className={styles.intro}> <div className={styles.intro}>
<Title level="h1">{pageData.heading}</Title> <Title as="h2" level="h1">
{pageData.heading}
</Title>
<Preamble>{pageData.elevatorPitch}</Preamble> <Preamble>{pageData.elevatorPitch}</Preamble>
</div> </div>
@@ -72,6 +74,7 @@ export default async function HotelSubpage({
<HotelSubpageAdditionalContent <HotelSubpageAdditionalContent
subpage={subpage} subpage={subpage}
hotel={hotelData.hotel} hotel={hotelData.hotel}
additionalData={hotelData.additionalData}
/> />
</main> </main>

View File

@@ -1,4 +1,5 @@
import { import {
accessibilitySubPage,
parkingSubPage, parkingSubPage,
wellnessSubPage, wellnessSubPage,
} from "@/constants/routes/hotelSubpages" } from "@/constants/routes/hotelSubpages"
@@ -44,6 +45,23 @@ export function getSubpageData(
} }
: null, : null,
} }
case accessibilitySubPage[lang]:
const accessibilityImage = additionalData.accessibility?.heroImages[0]
return {
...additionalData.hotelSpecialNeeds,
heading: intl.formatMessage(
{ id: "Accessibility at {hotel}" },
{
hotel: hotel.name,
}
),
heroImage: accessibilityImage
? {
src: accessibilityImage.imageSizes.medium,
alt: accessibilityImage.metaData.altText || "",
}
: null,
}
default: default:
return null return null
} }

View File

@@ -15,3 +15,12 @@ export const wellnessSubPage = {
fi: "spa", fi: "spa",
de: "Wellness", de: "Wellness",
} }
export const accessibilitySubPage = {
en: "accessibility",
sv: "tillganglighet",
no: "tilgjengelighet",
da: "tilgaengelighed",
fi: "saavutettavuus",
de: "barrierefreiheit",
}

View File

@@ -12,6 +12,7 @@
"About the hotel": "Om hotellet", "About the hotel": "Om hotellet",
"Accept new price": "Accepter ny pris", "Accept new price": "Accepter ny pris",
"Accessibility": "Tilgængelighed", "Accessibility": "Tilgængelighed",
"Accessibility at {hotel}": "Tilgængelighed på {hotel}",
"Accessible Room": "Tilgængelighedsrum", "Accessible Room": "Tilgængelighedsrum",
"Active": "Aktiv", "Active": "Aktiv",
"Activities": "Aktiviteter", "Activities": "Aktiviteter",

View File

@@ -12,6 +12,7 @@
"About the hotel": "Über das Hotel", "About the hotel": "Über das Hotel",
"Accept new price": "Neuen Preis akzeptieren", "Accept new price": "Neuen Preis akzeptieren",
"Accessibility": "Zugänglichkeit", "Accessibility": "Zugänglichkeit",
"Accessibility at {hotel}": "Barrierefreiheit im {hotel}",
"Accessible Room": "Barrierefreies Zimmer", "Accessible Room": "Barrierefreies Zimmer",
"Active": "Aktiv", "Active": "Aktiv",
"Activities": "Aktivitäten", "Activities": "Aktivitäten",

View File

@@ -12,6 +12,7 @@
"About the hotel": "About the hotel", "About the hotel": "About the hotel",
"Accept new price": "Accept new price", "Accept new price": "Accept new price",
"Accessibility": "Accessibility", "Accessibility": "Accessibility",
"Accessibility at {hotel}": "Accessibility at {hotel}",
"Accessible Room": "Accessibility room", "Accessible Room": "Accessibility room",
"Active": "Active", "Active": "Active",
"Activities": "Activities", "Activities": "Activities",

View File

@@ -12,6 +12,7 @@
"About the hotel": "Tietoja hotellista", "About the hotel": "Tietoja hotellista",
"Accept new price": "Hyväksy uusi hinta", "Accept new price": "Hyväksy uusi hinta",
"Accessibility": "Saavutettavuus", "Accessibility": "Saavutettavuus",
"Accessibility at {hotel}": "Esteettömyys {hotel}",
"Accessible Room": "Esteetön huone", "Accessible Room": "Esteetön huone",
"Active": "Aktiivinen", "Active": "Aktiivinen",
"Activities": "Aktiviteetit", "Activities": "Aktiviteetit",

View File

@@ -12,6 +12,7 @@
"About the hotel": "Om hotellet", "About the hotel": "Om hotellet",
"Accept new price": "Aksepterer ny pris", "Accept new price": "Aksepterer ny pris",
"Accessibility": "Tilgjengelighet", "Accessibility": "Tilgjengelighet",
"Accessibility at {hotel}": "Tilgjengelighet på {hotel}",
"Accessible Room": "Tilgjengelighetsrom", "Accessible Room": "Tilgjengelighetsrom",
"Active": "Aktiv", "Active": "Aktiv",
"Activities": "Aktiviteter", "Activities": "Aktiviteter",

View File

@@ -12,6 +12,7 @@
"About the hotel": "Om hotellet", "About the hotel": "Om hotellet",
"Accept new price": "Accepter ny pris", "Accept new price": "Accepter ny pris",
"Accessibility": "Tillgänglighet", "Accessibility": "Tillgänglighet",
"Accessibility at {hotel}": "Tillgänglighet på {hotel}",
"Accessible Room": "Tillgänglighetsrum", "Accessible Room": "Tillgänglighetsrum",
"Active": "Aktiv", "Active": "Aktiv",
"Activities": "Aktiviteter", "Activities": "Aktiviteter",

View File

@@ -34,6 +34,11 @@ export const extraPageSchema = z.object({
mainBody: z.string().optional(), mainBody: z.string().optional(),
}) })
export const accessibilitySchema = z.object({
headingText: z.string().default(""),
heroImages: z.array(imageSchema),
})
export const additionalDataSchema = z.object({ export const additionalDataSchema = z.object({
attributes: z.object({ attributes: z.object({
name: z.string(), name: z.string(),
@@ -56,6 +61,7 @@ export const additionalDataSchema = z.object({
hotelParking: extraPageSchema, hotelParking: extraPageSchema,
hotelSpecialNeeds: extraPageSchema, hotelSpecialNeeds: extraPageSchema,
hotelRoomElevatorPitchText: z.string().optional(), hotelRoomElevatorPitchText: z.string().optional(),
accessibility: accessibilitySchema.optional(),
}), }),
type: z.literal("additionalData"), type: z.literal("additionalData"),
}) })

View File

@@ -1,5 +1,4 @@
import type { Hotel } from "@/types/hotel"
export type AccessibilityAmenityProps = { export type AccessibilityAmenityProps = {
accessibility: Hotel["hotelFacts"]["hotelInformation"]["accessibility"] elevatorPitch?: string
hasExtraAccessibilityPage: boolean
} }

View File

@@ -1,11 +1,12 @@
import type { Hotel, Restaurant, RestaurantOpeningHours } from "@/types/hotel" import type { Hotel, Restaurant, RestaurantOpeningHours } from "@/types/hotel"
import type { AccessibilityAmenityProps } from "./accessibility"
import type { ParkingAmenityProps } from "./parking" import type { ParkingAmenityProps } from "./parking"
export type AmenitiesSidePeekProps = { export type AmenitiesSidePeekProps = {
amenitiesList: Hotel["detailedFacilities"] amenitiesList: Hotel["detailedFacilities"]
parking: ParkingAmenityProps parking: ParkingAmenityProps
checkInInformation: Hotel["hotelFacts"]["checkin"] checkInInformation: Hotel["hotelFacts"]["checkin"]
accessibility: Hotel["hotelFacts"]["hotelInformation"]["accessibility"] accessibility: AccessibilityAmenityProps
restaurants: Restaurant[] restaurants: Restaurant[]
} }