Merged in fix/SW-1524-clickable-area (pull request #2125)
Fix/SW-1524: Enter details- expand clickable area * fix(SW-1524): make whole price area clickable * fix(SW-1524): add div as fake button Approved-by: Bianca Widstam Approved-by: Erik Tiekstra
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { cx } from "class-variance-authority"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import Divider from "@/components/TempDesignSystem/Divider"
|
||||
@@ -13,6 +14,7 @@ import { RateTypeEnum } from "@/types/enums/rateType"
|
||||
export default function HotelPriceCard({
|
||||
productTypePrices,
|
||||
isMemberPrice = false,
|
||||
className,
|
||||
}: PriceCardProps) {
|
||||
const intl = useIntl()
|
||||
const isRegularOrPublicPromotionRate =
|
||||
@@ -20,7 +22,7 @@ export default function HotelPriceCard({
|
||||
productTypePrices.rateType === RateTypeEnum.PublicPromotion
|
||||
|
||||
return (
|
||||
<dl className={styles.priceCard}>
|
||||
<dl className={cx(styles.priceCard, className)}>
|
||||
{isRegularOrPublicPromotionRate &&
|
||||
(isMemberPrice ? (
|
||||
<div className={styles.priceRow}>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import styles from "./noPriceAvailable.module.css"
|
||||
|
||||
@@ -11,15 +10,15 @@ export default function NoPriceAvailableCard() {
|
||||
return (
|
||||
<div className={styles.priceCard}>
|
||||
<div className={styles.noRooms}>
|
||||
<div>
|
||||
<MaterialIcon icon="error" color="Icon/Interactive/Accent" />
|
||||
</div>
|
||||
<Caption color="uiTextHighContrast">
|
||||
{intl.formatMessage({
|
||||
defaultMessage:
|
||||
"There are no rooms available that match your request.",
|
||||
})}
|
||||
</Caption>
|
||||
<MaterialIcon icon="error" color="Icon/Feedback/Error" />
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
defaultMessage:
|
||||
"There are no rooms available that match your request.",
|
||||
})}
|
||||
</span>
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -71,10 +71,6 @@
|
||||
gap: var(--Spacing-x-half);
|
||||
}
|
||||
|
||||
.button {
|
||||
min-width: 160px;
|
||||
}
|
||||
|
||||
.specialAlerts {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -87,6 +83,22 @@
|
||||
gap: var(--Spacing-x-one-and-half);
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
.fakeButton {
|
||||
background-color: var(--Component-Button-Brand-Primary-Fill-Hover);
|
||||
border-color: var(--Component-Button-Brand-Primary-Border-Hover);
|
||||
color: var(--Component-Button-Brand-Primary-On-fill-Hover);
|
||||
}
|
||||
|
||||
.priceCard {
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
var(--Surface-Primary-Hover) 0%,
|
||||
var(--Surface-Primary-Hover) 100%
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.strikedText {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
@@ -97,6 +109,28 @@
|
||||
border-radius: var(--Corner-radius-md);
|
||||
}
|
||||
|
||||
.fakeButton {
|
||||
min-width: 160px;
|
||||
border-radius: var(--Corner-radius-rounded);
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--Space-x05);
|
||||
|
||||
padding: 10px var(--Space-x2);
|
||||
background-color: var(--Component-Button-Brand-Primary-Fill-Default);
|
||||
border-color: var(--Component-Button-Brand-Primary-Border-Default);
|
||||
color: var(--Component-Button-Brand-Primary-On-fill-Default);
|
||||
}
|
||||
|
||||
.fakeButton.disabled {
|
||||
background-color: var(--Component-Button-Brand-Primary-Fill-Disabled);
|
||||
border-color: var(--Component-Button-Brand-Primary-Border-Disabled);
|
||||
color: var(--Component-Button-Brand-Primary-On-fill-Disabled);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) and (max-width: 1024px) {
|
||||
.imageContainer {
|
||||
height: 180px;
|
||||
@@ -140,7 +174,7 @@
|
||||
margin-bottom: var(--Spacing-x-one-and-half);
|
||||
}
|
||||
|
||||
.pageListing .button {
|
||||
.pageListing .fakeButton {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { cx } from "class-variance-authority"
|
||||
import { useParams } from "next/dist/client/components/navigation"
|
||||
import { useRouter, useSearchParams } from "next/navigation"
|
||||
import { memo } from "react"
|
||||
@@ -14,7 +15,6 @@ import { useHotelsMapStore } from "@/stores/hotels-map"
|
||||
import BookingCodeChip from "@/components/BookingCodeChip"
|
||||
import { FacilityToIcon } from "@/components/ContentType/HotelPage/data"
|
||||
import ImageGallery from "@/components/ImageGallery"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Divider from "@/components/TempDesignSystem/Divider"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
@@ -79,6 +79,8 @@ function HotelCard({
|
||||
defaultMessage: "Not enough points",
|
||||
})
|
||||
|
||||
const isDisabled = price?.redemptions?.length && hasInsufficientPoints
|
||||
|
||||
return (
|
||||
<article
|
||||
className={classNames}
|
||||
@@ -172,7 +174,10 @@ function HotelCard({
|
||||
sidePeekKey={SidePeekEnum.hotelDetails}
|
||||
/>
|
||||
</section>
|
||||
<div className={styles.prices}>
|
||||
<PricesWrapper
|
||||
href={`${selectRate(lang)}?hotel=${hotel.operaId}`}
|
||||
isClickable={availability.productType && !isDisabled}
|
||||
>
|
||||
{!availability.productType ? (
|
||||
<NoPriceAvailableCard />
|
||||
) : (
|
||||
@@ -187,11 +192,15 @@ function HotelCard({
|
||||
!price?.member ||
|
||||
(bookingCode && !fullPrice)) &&
|
||||
price?.public && (
|
||||
<HotelPriceCard productTypePrices={price.public} />
|
||||
<HotelPriceCard
|
||||
productTypePrices={price.public}
|
||||
className={styles.priceCard}
|
||||
/>
|
||||
)}
|
||||
{availability.productType.member && (
|
||||
<HotelPriceCard
|
||||
productTypePrices={availability.productType.member}
|
||||
className={styles.priceCard}
|
||||
isMemberPrice
|
||||
/>
|
||||
)}
|
||||
@@ -222,47 +231,52 @@ function HotelCard({
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
{price?.redemptions?.length && hasInsufficientPoints ? (
|
||||
{isDisabled ? (
|
||||
<Tooltip
|
||||
arrow="left"
|
||||
position="bottom"
|
||||
text={notEnoughPointsLabel}
|
||||
>
|
||||
<Button
|
||||
theme="base"
|
||||
intent="primary"
|
||||
size="small"
|
||||
className={styles.button}
|
||||
disabled
|
||||
>
|
||||
{notEnoughPointsLabel}
|
||||
</Button>
|
||||
<div className={cx(styles.fakeButton, styles.disabled)}>
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<span>{notEnoughPointsLabel}</span>
|
||||
</Typography>
|
||||
</div>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Button
|
||||
asChild
|
||||
theme="base"
|
||||
intent="primary"
|
||||
size="small"
|
||||
className={styles.button}
|
||||
>
|
||||
<Link
|
||||
href={`${selectRate(lang)}?hotel=${hotel.operaId}`}
|
||||
color="none"
|
||||
keepSearchParams
|
||||
>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "See rooms",
|
||||
})}
|
||||
</Link>
|
||||
</Button>
|
||||
<div className={styles.fakeButton}>
|
||||
<Typography variant="Body/Paragraph/mdBold">
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "See rooms",
|
||||
})}
|
||||
</span>
|
||||
</Typography>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</PricesWrapper>
|
||||
</div>
|
||||
</article>
|
||||
)
|
||||
}
|
||||
|
||||
interface PricesWrapperProps {
|
||||
href: string
|
||||
isClickable?: boolean
|
||||
children: React.ReactNode
|
||||
}
|
||||
function PricesWrapper({ href, isClickable, children }: PricesWrapperProps) {
|
||||
const content = <div className={styles.prices}>{children}</div>
|
||||
|
||||
return isClickable ? (
|
||||
<Link href={href} color="none" className={styles.link} keepSearchParams>
|
||||
{content}
|
||||
</Link>
|
||||
) : (
|
||||
content
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(HotelCard)
|
||||
|
||||
@@ -7,6 +7,7 @@ import type {
|
||||
export type PriceCardProps = {
|
||||
productTypePrices: ProductTypePrices
|
||||
isMemberPrice?: boolean
|
||||
className?: string
|
||||
}
|
||||
|
||||
export type PointsRowProps = {
|
||||
|
||||
Reference in New Issue
Block a user