Merged in feat/SW-3289-replace-sidepeek-hotel-reservation (pull request #2686)
feat(SW-3289): replace sidepeek * fix(SW-3289): replace sidepeek * fix(SW-3289): add wrapping prop and change prop name to buttonVariant * fix(SW-3289): replace body with typography * fix(SW-3289): fix intl message Approved-by: Joakim Jäderberg
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import {
|
||||
BED_TYPE_ICONS,
|
||||
type BedTypes,
|
||||
} from "@scandic-hotels/booking-flow/bedTypeIcons"
|
||||
import { FacilityIcon } from "@scandic-hotels/design-system/Icons/FacilityIcon"
|
||||
import ImageGallery from "@scandic-hotels/design-system/ImageGallery"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import styles from "./roomSidePeekContent.module.css"
|
||||
|
||||
import type { ApiImage, Room } from "@scandic-hotels/trpc/types/hotel"
|
||||
|
||||
interface RoomSidePeekContentProps {
|
||||
room: Room
|
||||
}
|
||||
|
||||
export function RoomSidePeekContent({ room }: RoomSidePeekContentProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
const roomSize = room.roomSize
|
||||
const totalOccupancy = room.totalOccupancy
|
||||
const roomDescription = room.descriptions.medium
|
||||
const galleryImages = mapApiImagesToGalleryImages(room.images)
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.mainContent}>
|
||||
{totalOccupancy && (
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<p>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
defaultMessage:
|
||||
"Max. {max, plural, one {{range} guest} other {{range} guests}}",
|
||||
},
|
||||
{
|
||||
max: totalOccupancy.max,
|
||||
range: totalOccupancy.range,
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</Typography>
|
||||
)}
|
||||
{roomSize && (
|
||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||
<p>
|
||||
{roomSize.min === roomSize.max
|
||||
? intl.formatMessage(
|
||||
{
|
||||
defaultMessage: "{roomSize} m²",
|
||||
},
|
||||
{
|
||||
roomSize: roomSize.min,
|
||||
}
|
||||
)
|
||||
: intl.formatMessage(
|
||||
{
|
||||
defaultMessage: "{roomSizeMin}–{roomSizeMax} m²",
|
||||
},
|
||||
{
|
||||
roomSizeMin: roomSize.min,
|
||||
roomSizeMax: roomSize.max,
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</Typography>
|
||||
)}
|
||||
<div className={styles.imageContainer}>
|
||||
<ImageGallery images={galleryImages} title={room.name} height={280} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.listContainer}>
|
||||
<Typography variant="Title/Subtitle/md">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Room amenities",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<ul className={styles.facilityList}>
|
||||
{[...room.roomFacilities]
|
||||
.sort((a, b) => a.sortOrder - b.sortOrder)
|
||||
.map((facility) => {
|
||||
return (
|
||||
<li key={facility.name}>
|
||||
<FacilityIcon
|
||||
name={facility.icon}
|
||||
size={24}
|
||||
color="Icon/Default"
|
||||
/>
|
||||
<span>
|
||||
{facility.availableInAllRooms
|
||||
? facility.name
|
||||
: intl.formatMessage(
|
||||
{
|
||||
defaultMessage:
|
||||
"{facility} (available in some rooms)",
|
||||
},
|
||||
{
|
||||
facility: facility.name,
|
||||
}
|
||||
)}
|
||||
</span>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={styles.listContainer}>
|
||||
<Typography variant="Title/Subtitle/md">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Bed options",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "Subject to availability",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
<ul className={styles.bedOptions}>
|
||||
{room.roomTypes.map((roomType) => {
|
||||
const description =
|
||||
roomType.description || roomType.mainBed.description
|
||||
const MainBedIcon =
|
||||
BED_TYPE_ICONS[roomType.mainBed.type as BedTypes]
|
||||
const ExtraBedIcon = roomType.fixedExtraBed
|
||||
? BED_TYPE_ICONS[roomType.fixedExtraBed.type as BedTypes]
|
||||
: null
|
||||
return (
|
||||
<li key={roomType.code}>
|
||||
{MainBedIcon ? <MainBedIcon height={24} width={24} /> : null}
|
||||
{ExtraBedIcon ? <ExtraBedIcon height={24} width={30} /> : null}
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<span>{description}</span>
|
||||
</Typography>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
<div className={styles.listContainer}>
|
||||
<Typography variant="Title/Subtitle/md">
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "About the hotel",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p>{roomDescription}</p>
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function mapApiImagesToGalleryImages(apiImages: ApiImage[]) {
|
||||
return apiImages.map((apiImage) => ({
|
||||
src: apiImage.imageSizes.medium,
|
||||
alt:
|
||||
apiImage.metaData.altText ||
|
||||
apiImage.metaData.altText_En ||
|
||||
apiImage.metaData.title ||
|
||||
apiImage.metaData.title_En,
|
||||
caption: apiImage.metaData.title || apiImage.metaData.title_En,
|
||||
smallSrc: apiImage.imageSizes.small,
|
||||
}))
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
position: relative;
|
||||
margin-bottom: calc(
|
||||
var(--Spacing-x4) * 2 + 80px
|
||||
); /* Creates space between the wrapper and buttonContainer */
|
||||
}
|
||||
|
||||
.mainContent {
|
||||
color: var(--Text-Secondary);
|
||||
}
|
||||
|
||||
.mainContent,
|
||||
.listContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x-one-and-half);
|
||||
}
|
||||
|
||||
.imageContainer {
|
||||
position: relative;
|
||||
border-radius: var(--Corner-radius-md);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.imageContainer img {
|
||||
width: 100%;
|
||||
aspect-ratio: 16/9;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.facilityList {
|
||||
column-count: 2;
|
||||
column-gap: var(--Spacing-x2);
|
||||
color: var(--Text-Secondary);
|
||||
}
|
||||
.facilityList li > span:nth-child(2) {
|
||||
overflow: hidden;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.facilityList li {
|
||||
display: flex !important; /* Overrides the display none from grids.stackable on Hotel Page */
|
||||
gap: var(--Spacing-x1);
|
||||
margin-bottom: var(--Spacing-x-half);
|
||||
}
|
||||
|
||||
.bedOptions {
|
||||
color: var(--Text-Secondary);
|
||||
}
|
||||
|
||||
.bedOptions li {
|
||||
display: flex;
|
||||
gap: var(--Spacing-x1);
|
||||
margin-bottom: var(--Spacing-x-half);
|
||||
}
|
||||
|
||||
.facilityList li svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
Reference in New Issue
Block a user