fix(SW-2721): Facilities only uses 2 columns on viewports >= 768px
Approved-by: Matilda Landström
This commit is contained in:
@@ -17,7 +17,7 @@
|
|||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid:not(.allVisible) :nth-child(n + 4) {
|
.grid:not(.allVisible) > :nth-child(n + 4) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { cx } from "class-variance-authority"
|
||||||
|
import { useRef, useState } from "react"
|
||||||
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
|
import { FacilityIcon } from "@/components/SidePeeks/RoomSidePeek/facilityIcon"
|
||||||
|
import ShowMoreButton from "@/components/TempDesignSystem/ShowMoreButton"
|
||||||
|
|
||||||
|
import styles from "./roomFacilities.module.css"
|
||||||
|
|
||||||
|
import type { Room } from "@/types/hotel"
|
||||||
|
|
||||||
|
interface RoomFacilitiesProps {
|
||||||
|
roomFacilities: Room["roomFacilities"]
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function RoomFacilities({
|
||||||
|
roomFacilities,
|
||||||
|
}: RoomFacilitiesProps) {
|
||||||
|
const intl = useIntl()
|
||||||
|
const showMobileToggleButton = roomFacilities.length > 10
|
||||||
|
const [allVisibleOnMobile, setAllVisibleOnMobile] = useState(
|
||||||
|
!showMobileToggleButton
|
||||||
|
)
|
||||||
|
const scrollRef = useRef<HTMLHeadingElement>(null)
|
||||||
|
|
||||||
|
const mappedFacilities = roomFacilities
|
||||||
|
.sort((a, b) => a.sortOrder - b.sortOrder)
|
||||||
|
.map((facility) => {
|
||||||
|
const facilityName = facility.availableInAllRooms
|
||||||
|
? facility.name
|
||||||
|
: intl.formatMessage(
|
||||||
|
{
|
||||||
|
defaultMessage: "{facility} (available in some rooms)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
facility: facility.name,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
...facility,
|
||||||
|
name: facilityName,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleShowMore() {
|
||||||
|
if (scrollRef.current && allVisibleOnMobile) {
|
||||||
|
scrollRef.current.scrollIntoView({ behavior: "smooth" })
|
||||||
|
}
|
||||||
|
setAllVisibleOnMobile((state) => !state)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Typography variant="Title/Subtitle/md">
|
||||||
|
<h3 ref={scrollRef} className={styles.heading}>
|
||||||
|
{intl.formatMessage({
|
||||||
|
defaultMessage: "Room amenities",
|
||||||
|
})}
|
||||||
|
</h3>
|
||||||
|
</Typography>
|
||||||
|
<ul
|
||||||
|
className={cx(styles.facilities, {
|
||||||
|
[styles.allVisibleMobile]: allVisibleOnMobile,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{mappedFacilities.map((facility) => (
|
||||||
|
<li className={styles.item} key={facility.name}>
|
||||||
|
<FacilityIcon
|
||||||
|
name={facility.icon}
|
||||||
|
size={24}
|
||||||
|
color="CurrentColor"
|
||||||
|
className={styles.icon}
|
||||||
|
/>
|
||||||
|
<Typography variant="Body/Paragraph/mdRegular">
|
||||||
|
<span>{facility.name}</span>
|
||||||
|
</Typography>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
{showMobileToggleButton ? (
|
||||||
|
<div className={styles.ctaWrapper}>
|
||||||
|
<ShowMoreButton
|
||||||
|
loadMoreData={handleShowMore}
|
||||||
|
showLess={allVisibleOnMobile}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
.heading {
|
||||||
|
/* Custom value to make the heading visible with a bit of margin when scrolled programmatically */
|
||||||
|
scroll-margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--Spacing-x1);
|
||||||
|
margin-bottom: var(--Spacing-x-half);
|
||||||
|
align-items: self-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
color: var(--Text-Secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ctaWrapper {
|
||||||
|
width: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 767px) {
|
||||||
|
.facilities:not(.allVisibleMobile) > :nth-child(n + 11) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 768px) {
|
||||||
|
.facilities {
|
||||||
|
column-count: 2;
|
||||||
|
column-gap: var(--Spacing-x2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ctaWrapper {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import { cx } from "class-variance-authority"
|
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
|
|
||||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||||
@@ -9,7 +8,6 @@ import { dt } from "@/lib/dt"
|
|||||||
|
|
||||||
import ImageGallery from "@/components/ImageGallery"
|
import ImageGallery from "@/components/ImageGallery"
|
||||||
import { getBedIconName } from "@/components/SidePeeks/RoomSidePeek/bedIcon"
|
import { getBedIconName } from "@/components/SidePeeks/RoomSidePeek/bedIcon"
|
||||||
import { FacilityIcon } from "@/components/SidePeeks/RoomSidePeek/facilityIcon"
|
|
||||||
import Button from "@/components/TempDesignSystem/Button"
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
import SidePeek from "@/components/TempDesignSystem/SidePeek"
|
import SidePeek from "@/components/TempDesignSystem/SidePeek"
|
||||||
import { getIntl } from "@/i18n"
|
import { getIntl } from "@/i18n"
|
||||||
@@ -17,6 +15,7 @@ import { getLang } from "@/i18n/serverContext"
|
|||||||
import { mapApiImagesToGalleryImages } from "@/utils/imageGallery"
|
import { mapApiImagesToGalleryImages } from "@/utils/imageGallery"
|
||||||
|
|
||||||
import { getRoomNameAsParam } from "../../utils"
|
import { getRoomNameAsParam } from "../../utils"
|
||||||
|
import RoomFacilities from "./RoomFacilities"
|
||||||
|
|
||||||
import styles from "./room.module.css"
|
import styles from "./room.module.css"
|
||||||
|
|
||||||
@@ -95,45 +94,7 @@ export default async function RoomSidePeek({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.innerContent}>
|
<div className={styles.innerContent}>
|
||||||
<Typography variant="Title/Subtitle/md">
|
<RoomFacilities roomFacilities={room.roomFacilities} />
|
||||||
<h3>
|
|
||||||
{intl.formatMessage({
|
|
||||||
defaultMessage: "Room amenities",
|
|
||||||
})}
|
|
||||||
</h3>
|
|
||||||
</Typography>
|
|
||||||
<ul className={styles.facilityList}>
|
|
||||||
{room.roomFacilities
|
|
||||||
.sort((a, b) => a.sortOrder - b.sortOrder)
|
|
||||||
.map((facility) => {
|
|
||||||
const facilityName = facility.availableInAllRooms
|
|
||||||
? facility.name
|
|
||||||
: intl.formatMessage(
|
|
||||||
{
|
|
||||||
defaultMessage: "{facility} (available in some rooms)",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
facility: facility.name,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<li className={styles.listItem} key={facility.name}>
|
|
||||||
<FacilityIcon
|
|
||||||
name={facility.icon}
|
|
||||||
size={24}
|
|
||||||
color="Icon/Default"
|
|
||||||
/>
|
|
||||||
<Typography
|
|
||||||
variant="Body/Paragraph/mdRegular"
|
|
||||||
className={styles.iconText}
|
|
||||||
>
|
|
||||||
<span>{facilityName}</span>
|
|
||||||
</Typography>
|
|
||||||
</li>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.innerContent}>
|
<div className={styles.innerContent}>
|
||||||
@@ -155,16 +116,15 @@ export default async function RoomSidePeek({
|
|||||||
</div>
|
</div>
|
||||||
<ul className={styles.bedOptions}>
|
<ul className={styles.bedOptions}>
|
||||||
{room.roomTypes.map((roomType) => {
|
{room.roomTypes.map((roomType) => {
|
||||||
const bedIcon = getBedIconName(roomType.mainBed.type)
|
const iconName = getBedIconName(roomType.mainBed.type)
|
||||||
return (
|
return (
|
||||||
<li className={styles.listItem} key={roomType.code}>
|
<li className={styles.listItem} key={roomType.code}>
|
||||||
<MaterialIcon color="Icon/Default" icon={bedIcon} />
|
<MaterialIcon
|
||||||
<Typography
|
color="CurrentColor"
|
||||||
variant="Body/Paragraph/mdRegular"
|
icon={iconName}
|
||||||
className={cx(styles.iconText, {
|
className={styles.icon}
|
||||||
[styles.noIcon]: !bedIcon,
|
/>
|
||||||
})}
|
<Typography variant="Body/Paragraph/mdRegular">
|
||||||
>
|
|
||||||
<span>{roomType.mainBed.description}</span>
|
<span>{roomType.mainBed.description}</span>
|
||||||
</Typography>
|
</Typography>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -26,11 +26,6 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.facilityList {
|
|
||||||
column-count: 2;
|
|
||||||
column-gap: var(--Spacing-x2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.bedOptions {
|
.bedOptions {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -43,6 +38,11 @@
|
|||||||
margin-bottom: var(--Spacing-x-half);
|
margin-bottom: var(--Spacing-x-half);
|
||||||
align-items: self-start;
|
align-items: self-start;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
color: var(--Text-Secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.buttonContainer {
|
.buttonContainer {
|
||||||
@@ -54,8 +54,3 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconText {
|
|
||||||
color: var(--Text-Secondary);
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user