Merged in fix/STAY-135 (pull request #3368)

Fix/STAY-135 & STAY-127

* fix: make quantity and delivery separate steps in mobile

* fix: update design for delivery step in ancillary flow

* fix: add error state for missing time

* fix: only allow points or cash payment for ancillaries

* fix: break out stepper to design system

* fix: update design of select quantity step in add ancillaries flow

* fix: add error states for quantity

* fix: handle insufficient points case

* fix: update stepper to include optional disabledMessage tooltip

* fix: handle validations

* fix: change name to camel case


Approved-by: Bianca Widstam
Approved-by: Chuma Mcphoy (We Ahead)
This commit is contained in:
Christel Westerberg
2025-12-18 13:31:43 +00:00
parent 2c8b920dd8
commit 6b08d5a113
54 changed files with 1498 additions and 872 deletions

View File

@@ -4,8 +4,7 @@ import { useFormContext } from "react-hook-form"
import { useIntl } from "react-intl"
import Caption from "@scandic-hotels/design-system/Caption"
import Counter from "../Counter"
import Stepper from "@scandic-hotels/design-system/Stepper"
import styles from "./adult-selector.module.css"
@@ -42,7 +41,7 @@ export default function AdultSelector({
<Caption color="uiTextHighContrast" type="bold">
{adultsLabel}
</Caption>
<Counter
<Stepper
count={currentAdults}
handleOnDecrease={decreaseAdultsCount}
handleOnIncrease={increaseAdultsCount}

View File

@@ -4,8 +4,8 @@ import { useFormContext } from "react-hook-form"
import { useIntl } from "react-intl"
import Caption from "@scandic-hotels/design-system/Caption"
import Stepper from "@scandic-hotels/design-system/Stepper"
import Counter from "../Counter"
import ChildInfoSelector from "./ChildInfoSelector"
import styles from "./child-selector.module.css"
@@ -58,7 +58,7 @@ export default function ChildSelector({
<Caption color="uiTextHighContrast" type="bold">
{childrenLabel}
</Caption>
<Counter
<Stepper
count={currentChildren.length}
handleOnDecrease={() => {
decreaseChildrenCount(roomIndex)

View File

@@ -1,47 +0,0 @@
"use client"
import { IconButton } from "@scandic-hotels/design-system/IconButton"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography"
import styles from "./counter.module.css"
type CounterProps = {
count: number
handleOnIncrease: () => void
handleOnDecrease: () => void
disableIncrease: boolean
disableDecrease: boolean
}
export default function Counter({
count,
handleOnIncrease,
handleOnDecrease,
disableIncrease,
disableDecrease,
}: CounterProps) {
return (
<div className={styles.counterContainer}>
<IconButton
className={styles.counterBtn}
onPress={handleOnDecrease}
variant="Elevated"
isDisabled={disableDecrease}
>
<MaterialIcon icon="remove" color="CurrentColor" />
</IconButton>
<Typography variant="Body/Paragraph/mdRegular">
<p>{count}</p>
</Typography>
<IconButton
className={styles.counterBtn}
onPress={handleOnIncrease}
variant="Elevated"
isDisabled={disableIncrease}
>
<MaterialIcon icon="add" color="CurrentColor" />
</IconButton>
</div>
)
}

View File

@@ -5,7 +5,7 @@ import { Typography } from '../Typography'
import { TypographyProps } from '../Typography/types'
interface BadgeProps extends VariantProps<typeof config> {
number: number
number: string | number
}
export function Badge({ number, color, size }: BadgeProps) {

View File

@@ -12,9 +12,10 @@ type FocalPoint = {
y: number
}
export type ImageProps = NextImageProps & {
export type ImageProps = Omit<NextImageProps, 'src'> & {
focalPoint?: FocalPoint
dimensions?: { width: number; height: number }
src: NextImageProps['src'] | undefined
}
// Next/Image adds & instead of ? before the params

View File

@@ -9,9 +9,17 @@ interface RadioProps extends PropsWithChildren {
id?: string
isDisabled?: boolean
color?: 'Burgundy'
wrapping?: boolean
}
export function Radio({ id, value, children, color, isDisabled }: RadioProps) {
export function Radio({
id,
value,
children,
color,
isDisabled,
wrapping = true,
}: RadioProps) {
const inputId = id || `radio-${value}`
const classNames = variants({
@@ -23,10 +31,13 @@ export function Radio({ id, value, children, color, isDisabled }: RadioProps) {
id={inputId}
value={value}
isDisabled={isDisabled}
className={cx(styles.container, { [styles.disabled]: isDisabled })}
className={cx(styles.container, {
[styles.disabled]: isDisabled,
[styles.wrapping]: wrapping,
})}
>
<div className={`${styles.radio} ${classNames}`} />
<div>{children}</div>
{children && <div>{children}</div>}
</AriaRadio>
)
}

View File

@@ -2,10 +2,14 @@
display: flex;
align-items: center;
gap: var(--Space-x15);
padding: var(--Space-x1) 0;
cursor: pointer;
}
.wrapping {
padding: var(--Space-x1) 0;
}
.radio {
position: relative;
width: 24px;

View File

@@ -0,0 +1,58 @@
import { IconButton } from '../IconButton'
import { MaterialIcon } from '../Icons/MaterialIcon'
import { Tooltip } from '../Tooltip'
import { Typography } from '../Typography'
import styles from './stepper.module.css'
type StepperProps = {
count: number
handleOnIncrease: () => void
handleOnDecrease: () => void
disableIncrease: boolean
disableDecrease: boolean
disabledMessage?: string
}
export default function Stepper({
count,
handleOnIncrease,
handleOnDecrease,
disableIncrease,
disableDecrease,
disabledMessage,
}: StepperProps) {
return (
<div className={styles.counterContainer}>
<IconButton
className={styles.counterBtn}
onPress={handleOnDecrease}
variant="Elevated"
isDisabled={disableDecrease}
>
<MaterialIcon icon="remove" color="CurrentColor" />
</IconButton>
<div className={styles.countDisplay}>
<Typography variant="Body/Paragraph/mdRegular">
<p>{count}</p>
</Typography>
</div>
<Tooltip
text={disabledMessage}
isVisible={Boolean(disabledMessage && disableIncrease)}
position="top"
arrow="right"
isTouchable
>
<IconButton
className={styles.counterBtn}
onPress={handleOnIncrease}
variant="Elevated"
isDisabled={disableIncrease}
>
<MaterialIcon icon="add" color="CurrentColor" />
</IconButton>
</Tooltip>
</div>
)
}

View File

@@ -2,7 +2,7 @@
display: flex;
justify-content: flex-end;
align-items: center;
gap: 20px;
gap: var(--Space-x1);
color: var(--Text-Interactive-Default);
}
.counterBtn {
@@ -12,3 +12,8 @@
.counterBtn:not([disabled]) {
box-shadow: 0px 0px 8px 1px rgba(0, 0, 0, 0.1);
}
.countDisplay {
width: 20px;
text-align: center;
}

View File

@@ -43,6 +43,9 @@ export function Tooltip<P extends TooltipPosition>({
function handleToggle() {
setIsActive((prevState) => !prevState)
setTimeout(() => {
setIsActive(false)
}, 3000)
}
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {

View File

@@ -44,6 +44,10 @@
right: 0;
}
.top.arrowRight {
right: 0;
}
.tooltip::before {
content: '';
position: absolute;

View File

@@ -276,7 +276,7 @@
font-style: normal;
font-weight: 400;
font-display: block;
src: url(/_static/shared/fonts/material-symbols/rounded-b1df8938.woff2)
src: url(/_static/shared/fonts/material-symbols/rounded-3e10d67b.woff2)
format('woff2');
}

View File

@@ -165,6 +165,7 @@
"./Payment/PaymentMethodIcon": "./lib/components/Payment/PaymentMethodIcon.tsx",
"./PointsRateCard": "./lib/components/RateCard/Points/index.tsx",
"./Progress": "./lib/components/Progress/index.tsx",
"./Radio": "./lib/components/Radio/index.tsx",
"./RegularRateCard": "./lib/components/RateCard/Regular/index.tsx",
"./Select": "./lib/components/Select/index.tsx",
"./SidePeek": "./lib/components/SidePeek/index.tsx",
@@ -172,6 +173,7 @@
"./SidePeekSelfControlled": "./lib/components/SidePeek/SelfControlled.tsx",
"./SkeletonShimmer": "./lib/components/SkeletonShimmer/index.tsx",
"./StaticMap": "./lib/components/StaticMap/index.tsx",
"./Stepper": "./lib/components/Stepper/index.tsx",
"./Subtitle": "./lib/components/Subtitle/index.tsx",
"./Switch": "./lib/components/Switch/index.tsx",
"./Table": "./lib/components/Table/index.tsx",

View File

@@ -496,6 +496,7 @@ export const ancillaryPackagesSchema = z
translatedCategoryName: ancillary.categoryName,
internalCategoryName: ancillary.internalCategoryName,
requiresQuantity: getRequiresQuantity(item.id),
unitName: item.unitName,
})),
}))
.filter((ancillary) => ancillary.ancillaryContent.length > 0)