Merged in feat/SW-673-galleryicon-hotel-lightbox (pull request #734)
Feat/SW-673 galleryicon hotel lightbox * feat(SW-673): add galleryChip to trigger lightbox * feat(SW-673): add updated design galleryIcon * feat(SW-673): add first image from hotelContent and heroImages * feat(SW-673): fix import type * feat(SW-673): fix css variables * feat(SW-673): change component to include image that trigger lightbox * feat(SW-673): refactor name to imageGallery Approved-by: Niclas Edenvin
This commit is contained in:
@@ -14,15 +14,16 @@
|
||||
|
||||
.imageContainer {
|
||||
grid-area: image;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 116px;
|
||||
}
|
||||
|
||||
.tripAdvisor {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.image {
|
||||
height: 100%;
|
||||
width: 116px;
|
||||
.imageContainer img {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
@@ -77,6 +78,8 @@
|
||||
|
||||
.imageContainer {
|
||||
position: relative;
|
||||
min-height: 200px;
|
||||
width: 518px;
|
||||
}
|
||||
|
||||
.tripAdvisor {
|
||||
@@ -86,10 +89,6 @@
|
||||
top: 7px;
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 518px;
|
||||
}
|
||||
|
||||
.hotelInformation {
|
||||
padding-top: var(--Spacing-x2);
|
||||
padding-right: var(--Spacing-x2);
|
||||
|
||||
@@ -11,10 +11,11 @@ import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import ReadMore from "../ReadMore"
|
||||
import ImageGallery from "../SelectRate/ImageGallery"
|
||||
|
||||
import styles from "./hotelCard.module.css"
|
||||
|
||||
import { HotelCardProps } from "@/types/components/hotelReservation/selectHotel/hotelCardProps"
|
||||
import type { HotelCardProps } from "@/types/components/hotelReservation/selectHotel/hotelCardProps"
|
||||
|
||||
export default async function HotelCard({ hotel }: HotelCardProps) {
|
||||
const intl = await getIntl()
|
||||
@@ -27,13 +28,15 @@ export default async function HotelCard({ hotel }: HotelCardProps) {
|
||||
return (
|
||||
<article className={styles.card}>
|
||||
<section className={styles.imageContainer}>
|
||||
<Image
|
||||
src={hotelData.hotelContent.images.imageSizes.medium}
|
||||
alt={hotelData.hotelContent.images.metaData.altText}
|
||||
width={300}
|
||||
height={200}
|
||||
className={styles.image}
|
||||
/>
|
||||
{hotelData.gallery && (
|
||||
<ImageGallery
|
||||
title={hotelData.name}
|
||||
images={[
|
||||
hotelData.hotelContent.images,
|
||||
...hotelData.gallery.heroImages,
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
<div className={styles.tripAdvisor}>
|
||||
<Chip intent="primary" className={styles.tripAdvisor}>
|
||||
<TripAdvisorIcon color="white" />
|
||||
|
||||
@@ -12,10 +12,6 @@
|
||||
gap: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.image {
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
}
|
||||
|
||||
.imageWrapper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
@@ -24,6 +20,10 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.imageWrapper img {
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
}
|
||||
|
||||
.tripAdvisor {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -10,6 +10,7 @@ import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||
|
||||
import ReadMore from "../../ReadMore"
|
||||
import ImageGallery from "../ImageGallery"
|
||||
|
||||
import styles from "./hotelInfoCard.module.css"
|
||||
|
||||
@@ -28,12 +29,6 @@ export default function HotelInfoCard({ hotelData }: HotelInfoCardProps) {
|
||||
{hotelAttributes && (
|
||||
<section className={styles.wrapper}>
|
||||
<div className={styles.imageWrapper}>
|
||||
<Image
|
||||
src={hotelAttributes.hotelContent.images.imageSizes.medium}
|
||||
alt={hotelAttributes.hotelContent.images.metaData.altText}
|
||||
className={styles.image}
|
||||
fill
|
||||
/>
|
||||
{hotelAttributes.ratings?.tripAdvisor && (
|
||||
<div className={styles.tripAdvisor}>
|
||||
<TripAdvisorIcon color="burgundy" />
|
||||
@@ -42,7 +37,15 @@ export default function HotelInfoCard({ hotelData }: HotelInfoCardProps) {
|
||||
</Caption>
|
||||
</div>
|
||||
)}
|
||||
{/* TODO: gallery icon and image carousel */}
|
||||
{hotelAttributes.gallery && (
|
||||
<ImageGallery
|
||||
title={hotelAttributes.name}
|
||||
images={[
|
||||
hotelAttributes.hotelContent.images,
|
||||
...hotelAttributes.gallery.heroImages,
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.hotelContent}>
|
||||
<div className={styles.hotelInformation}>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
.galleryIcon {
|
||||
position: absolute;
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
max-height: 32px;
|
||||
width: 48px;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
padding: var(--Spacing-x-quarter) var(--Spacing-x-half);
|
||||
border-radius: var(--Corner-radius-Small);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x-quarter);
|
||||
}
|
||||
|
||||
.triggerArea {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { GalleryIcon } from "@/components/Icons"
|
||||
import Image from "@/components/Image"
|
||||
import Lightbox from "@/components/Lightbox"
|
||||
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
||||
|
||||
import styles from "./imageGallery.module.css"
|
||||
|
||||
import type { ImageGalleryProps } from "@/types/components/hotelReservation/selectRate/imageGallery"
|
||||
|
||||
export default function ImageGallery({ images, title }: ImageGalleryProps) {
|
||||
return (
|
||||
<Lightbox
|
||||
images={images.map((image) => ({
|
||||
url: image.imageSizes.small,
|
||||
alt: image.metaData.altText,
|
||||
title: image.metaData.title,
|
||||
}))}
|
||||
dialogTitle={title}
|
||||
>
|
||||
<div className={styles.triggerArea} id="lightboxTrigger">
|
||||
<Image
|
||||
src={images[0].imageSizes.medium}
|
||||
alt={images[0].metaData.altText}
|
||||
className={styles.image}
|
||||
fill
|
||||
/>
|
||||
<div className={styles.galleryIcon}>
|
||||
<GalleryIcon color="white" />
|
||||
<Footnote color="white" type="label">
|
||||
{images.length}
|
||||
</Footnote>
|
||||
</div>
|
||||
</div>
|
||||
</Lightbox>
|
||||
)
|
||||
}
|
||||
@@ -5,18 +5,18 @@ import { useIntl } from "react-intl"
|
||||
import { RateDefinition } from "@/server/routers/hotels/output"
|
||||
|
||||
import FlexibilityOption from "@/components/HotelReservation/SelectRate/RoomSelection/FlexibilityOption"
|
||||
import { ChevronRightSmallIcon, GalleryIcon } from "@/components/Icons"
|
||||
import Image from "@/components/Image"
|
||||
import Lightbox from "@/components/Lightbox"
|
||||
import { ChevronRightSmallIcon } from "@/components/Icons"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
|
||||
import ImageGallery from "../../ImageGallery"
|
||||
|
||||
import styles from "./roomCard.module.css"
|
||||
|
||||
import { RoomCardProps } from "@/types/components/hotelReservation/selectRate/roomCard"
|
||||
import type { RoomCardProps } from "@/types/components/hotelReservation/selectRate/roomCard"
|
||||
|
||||
export default function RoomCard({
|
||||
rateDefinitions,
|
||||
@@ -25,7 +25,6 @@ export default function RoomCard({
|
||||
handleSelectRate,
|
||||
}: RoomCardProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
const saveRate = rateDefinitions.find(
|
||||
// TODO: Update string when API has decided
|
||||
(rate) => rate.cancellationRule === "NonCancellable"
|
||||
@@ -153,26 +152,8 @@ export default function RoomCard({
|
||||
)}
|
||||
{/*NOTE: images from the test API are hosted on test3.scandichotels.com,
|
||||
which can't be accessed unless on Scandic's Wifi or using Citrix. */}
|
||||
<Image
|
||||
src={mainImage.imageSizes.small}
|
||||
alt={mainImage.metaData.altText}
|
||||
width={330}
|
||||
height={185}
|
||||
/>
|
||||
{images && (
|
||||
<Lightbox
|
||||
images={images.map((image) => ({
|
||||
url: image.imageSizes.small,
|
||||
alt: image.metaData.altText,
|
||||
title: image.metaData.title,
|
||||
}))}
|
||||
dialogTitle={roomConfiguration.roomType}
|
||||
>
|
||||
<div className={styles.galleryIcon} id="lightboxTrigger">
|
||||
<GalleryIcon color="white" />
|
||||
<Footnote color="white">{images.length}</Footnote>
|
||||
</div>
|
||||
</Lightbox>
|
||||
<ImageGallery images={images} title={roomConfiguration.roomType} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -77,17 +77,3 @@
|
||||
min-height: 185px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.galleryIcon {
|
||||
position: absolute;
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
height: 24px;
|
||||
background-color: rgba(64, 57, 55, 0.9);
|
||||
padding: 0 var(--Spacing-x-half);
|
||||
border-radius: var(--Corner-radius-Small);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x-quarter);
|
||||
}
|
||||
|
||||
@@ -162,6 +162,21 @@ export const facilitySchema = z.object({
|
||||
),
|
||||
})
|
||||
|
||||
export const gallerySchema = z.object({
|
||||
heroImages: z.array(
|
||||
z.object({
|
||||
metaData: imageMetaDataSchema,
|
||||
imageSizes: imageSizesSchema,
|
||||
})
|
||||
),
|
||||
smallerImages: z.array(
|
||||
z.object({
|
||||
metaData: imageMetaDataSchema,
|
||||
imageSizes: imageSizesSchema,
|
||||
})
|
||||
),
|
||||
})
|
||||
|
||||
const healthFacilitySchema = z.object({
|
||||
type: z.string(),
|
||||
content: z.object({
|
||||
@@ -441,6 +456,7 @@ export const getHotelDataSchema = z.object({
|
||||
conferencesAndMeetings: facilitySchema.optional(),
|
||||
healthAndWellness: facilitySchema.optional(),
|
||||
restaurantImages: facilitySchema.optional(),
|
||||
gallery: gallerySchema.optional(),
|
||||
}),
|
||||
relationships: relationshipsSchema,
|
||||
}),
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import type { GalleryImages } from "@/types/hotel"
|
||||
|
||||
export type ImageGalleryProps = { images: GalleryImages; title: string }
|
||||
@@ -2,6 +2,7 @@ import { z } from "zod"
|
||||
|
||||
import {
|
||||
facilitySchema,
|
||||
gallerySchema,
|
||||
getHotelDataSchema,
|
||||
parkingSchema,
|
||||
pointOfInterestSchema,
|
||||
@@ -13,7 +14,6 @@ export type HotelData = z.infer<typeof getHotelDataSchema>
|
||||
export type Hotel = HotelData["data"]["attributes"]
|
||||
export type HotelAddress = HotelData["data"]["attributes"]["address"]
|
||||
export type HotelLocation = HotelData["data"]["attributes"]["location"]
|
||||
|
||||
export type Amenities = HotelData["data"]["attributes"]["detailedFacilities"]
|
||||
|
||||
type HotelRatings = HotelData["data"]["attributes"]["ratings"]
|
||||
@@ -22,6 +22,8 @@ export type HotelTripAdvisor =
|
||||
| undefined
|
||||
|
||||
export type RoomData = z.infer<typeof roomSchema>
|
||||
export type GallerySchema = z.infer<typeof gallerySchema>
|
||||
export type GalleryImages = GallerySchema["heroImages"]
|
||||
|
||||
export type PointOfInterest = z.output<typeof pointOfInterestSchema>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user