This commit is contained in:
Tobias Johansson
2025-03-25 17:27:09 +01:00
committed by Simon Emanuelsson
parent 93962e4c59
commit 310a5a7a68
17 changed files with 914 additions and 523 deletions

View File

@@ -0,0 +1,129 @@
import type { Meta, StoryObj } from '@storybook/react'
import PointsRateCard from '.'
const meta: Meta<typeof PointsRateCard> = {
title: 'Components/RateCard/Points',
component: PointsRateCard,
decorators: [
(Story) => (
<div style={{ maxWidth: '400px' }}>
<Story />
</div>
),
],
argTypes: {
rateTitle: { control: 'text' },
paymentTerm: { control: 'text' },
bannerText: { control: 'text' },
rates: { control: 'object' },
selectedRate: { control: 'text' },
onRateSelect: { action: 'onRateSelect' },
isNotEnoughPoints: { control: 'boolean' },
notEnoughPointsText: { control: 'text' },
},
}
export default meta
type Story = StoryObj<typeof PointsRateCard>
export const Default: Story = {
args: {
rateTitle: 'FREE CANCELLATION',
paymentTerm: 'PAY LATER',
bannerText: 'Reward night ∙ Breakfast included',
rates: [
{
points: '20000',
currency: 'PTS',
},
{
points: '15000',
currency: 'PTS',
additionalPrice: {
price: '250',
currency: 'EUR',
},
},
{
points: '10000',
currency: 'PTS',
additionalPrice: {
price: '500',
currency: 'EUR',
},
},
],
selectedRate: undefined,
onRateSelect: (value) => console.log(value),
},
}
export const WithDisabledRates: Story = {
args: {
rateTitle: 'FREE CANCELLATION',
paymentTerm: 'PAY LATER',
bannerText: 'Reward night ∙ Breakfast included',
rates: [
{
points: '20000',
currency: 'PTS',
isDisabled: true,
},
{
points: '15000',
currency: 'PTS',
isDisabled: true,
additionalPrice: {
price: '250',
currency: 'EUR',
},
},
{
points: '10000',
currency: 'PTS',
additionalPrice: {
price: '500',
currency: 'EUR',
},
},
],
selectedRate: '2',
onRateSelect: (value) => console.log(value),
},
}
export const NotEnoughPoints: Story = {
args: {
rateTitle: 'FREE CANCELLATION',
paymentTerm: 'PAY LATER',
bannerText: 'Reward night ∙ Breakfast included',
rates: [
{
points: '20000',
currency: 'PTS',
},
{
points: '15000',
currency: 'PTS',
additionalPrice: {
price: '250',
currency: 'EUR',
},
},
{
points: '10000',
currency: 'PTS',
additionalPrice: {
price: '500',
currency: 'EUR',
},
},
],
selectedRate: undefined,
isNotEnoughPoints: true,
notEnoughPointsText: 'Not enough points',
onRateSelect: (value) => console.log(value),
},
}

View File

@@ -0,0 +1,113 @@
import { Typography } from '../../Typography'
import { RatePointsOption } from '../types'
import styles from '../rate-card.module.css'
import { Button } from '../../Button'
import InfoCircleIcon from '../../Icons/InfoCircle'
import InfoCircleFilledIcon from '../../Icons/InfoCircleFilled'
import { Radio } from '../../Radio'
import { RadioGroup } from 'react-aria-components'
import { variants } from '../variants'
interface PointsRateCardProps {
rateTitle: string
paymentTerm: string
bannerText: string
rates: RatePointsOption[]
selectedRate: string | undefined
onRateSelect: (value: string) => void
isNotEnoughPoints?: boolean
notEnoughPointsText?: string
handleTermsClick?: () => void
}
export default function PointsRateCard({
rateTitle,
paymentTerm,
bannerText,
rates,
selectedRate,
isNotEnoughPoints,
notEnoughPointsText,
onRateSelect,
handleTermsClick,
}: PointsRateCardProps) {
const classNames = variants({
variant: 'Points',
})
return (
<div className={classNames}>
<Typography variant="Tag/sm">
<p className={styles.banner}>{bannerText}</p>
</Typography>
<div className={styles.container}>
<header>
<Typography variant="Tag/sm">
<h3 className={styles.title}>
<Button
variant="Icon"
color="IconDefault"
size="Small"
onPress={handleTermsClick}
>
<InfoCircleIcon height={20} width={20} />
</Button>
{rateTitle}
<span className={styles.textSecondary}>
{` / ${paymentTerm}`}
</span>
</h3>
</Typography>
</header>
<div className={styles.content}>
<RadioGroup value={selectedRate} onChange={onRateSelect}>
{rates.map((rate, index) => (
<div key={index} className={styles.rateRow}>
<Radio
value={index.toString()}
isDisabled={rate.isDisabled || isNotEnoughPoints}
>
<div className={styles.pointsRow}>
<Typography variant="Title/Subtitle/md">
<p>
{rate.points}{' '}
<Typography variant="Body/Supporting text (caption)/smBold">
<span>
{rate.currency} {rate.additionalPrice && ' + '}
</span>
</Typography>
</p>
</Typography>
{rate.additionalPrice && (
<Typography variant="Title/Subtitle/md">
<p>
{rate.additionalPrice.price}{' '}
<Typography variant="Body/Supporting text (caption)/smBold">
<span>{rate.currency}</span>
</Typography>
</p>
</Typography>
)}
</div>
</Radio>
</div>
))}
</RadioGroup>
</div>
{isNotEnoughPoints ? (
<footer className={styles.footer}>
<Typography variant="Body/Supporting text (caption)/smBold">
<p className={styles.notEnoughPoints}>
<div className={styles.filledIcon}>
<InfoCircleFilledIcon height={20} width={20} />
</div>
{notEnoughPointsText}
</p>
</Typography>
</footer>
) : null}
</div>
</div>
)
}