Merged in feature/SW-1736-foating-booking-widget (pull request #1696)

Feature/SW-1736 floating booking widget

* feature: Add floating booking widget on start page SW-1736

* fix: Make sure we don't try to use IntersectionObserver on the server

* fix: make sure that we disconnect the intersectionobserver when dismounting

* fix: pass searchparams to floating bookingwidget


Approved-by: Michael Zetterberg
This commit is contained in:
Joakim Jäderberg
2025-04-04 06:52:37 +00:00
parent 7b1760ca17
commit 3c810d67a2
17 changed files with 243 additions and 21 deletions

View File

@@ -1,6 +1,7 @@
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { cva } from "class-variance-authority"
import { use, useEffect, useRef, useState } from "react"
import { FormProvider, useForm } from "react-hook-form"
@@ -126,6 +127,17 @@ export default function BookingWidgetClient({
reValidateMode: "onSubmit",
})
useEffect(() => {
if (!selectedLocation) return
/*
If `trpc.hotel.locations.get.useQuery` hasn't been fetched previously and is hence async
we need to update the default values when data is available
*/
methods.setValue("search", selectedLocation.name)
methods.setValue("location", JSON.stringify(selectedLocation))
}, [selectedLocation, methods])
function closeMobileSearch() {
setIsOpen(false)
document.body.style.overflowY = "visible"
@@ -190,7 +202,7 @@ export default function BookingWidgetClient({
}, [methods, selectedBookingCode])
if (isLoading) {
return <BookingWidgetSkeleton />
return <BookingWidgetSkeleton type={type} />
}
if (!isSuccess || !locations) {
@@ -198,13 +210,13 @@ export default function BookingWidgetClient({
return null
}
const classNames = bookingWidgetContainerVariants({
type,
})
return (
<FormProvider {...methods}>
<section
ref={bookingWidgetRef}
className={styles.wrapper}
data-open={isOpen}
>
<section ref={bookingWidgetRef} className={classNames} data-open={isOpen}>
<MobileToggleButton openMobileSearch={openMobileSearch} />
<div className={styles.formContainer}>
<button
@@ -222,13 +234,21 @@ export default function BookingWidgetClient({
)
}
export function BookingWidgetSkeleton() {
export function BookingWidgetSkeleton({
type = "full",
}: {
type?: BookingWidgetClientProps["type"]
}) {
const classNames = bookingWidgetContainerVariants({
type,
})
return (
<>
<section className={styles.wrapper} style={{ top: 0 }}>
<section className={classNames} style={{ top: 0 }}>
<MobileToggleButtonSkeleton />
<div className={styles.formContainer}>
<BookingWidgetFormSkeleton />
<BookingWidgetFormSkeleton type={type} />
</div>
</section>
</>
@@ -253,3 +273,16 @@ function getLocationObj(locations: Location[], destination: string) {
}
return null
}
export const bookingWidgetContainerVariants = cva(styles.wrapper, {
variants: {
type: {
default: styles.default,
full: styles.full,
compact: styles.compact,
},
},
defaultVariants: {
type: "full",
},
})