From 169094fc37c04437a81f3e1e8d945fe812cb8ac2 Mon Sep 17 00:00:00 2001 From: "Chuma Mcphoy (We Ahead)" Date: Thu, 24 Apr 2025 11:22:36 +0000 Subject: [PATCH] Merged in refactor/SW-2476-use-react-aria-radio-group-for-payment-options (pull request #1849) Refactor(SW-2177): Use react aria RadioGroup & Radio for payment options * fix(SW-SW-2177): enhance accessibility for payment options * Added keyboard navigation support to payment options. * Updated CSS to improve focus styles for payment option labels. * refactor: use RadioGroup & Radio from react aria for payment options * refactor(SW-2177): replace setValue and watch with useController for payment method handling * fix(SW-2177): remove comment and use cx for styles on PaymentOption * fix(SW-2177): Add keyboard focus indicator to payment option Approved-by: Michael Zetterberg Approved-by: Erik Tiekstra --- .../EnterDetails/Confirm/index.tsx | 26 ++++---- .../Payment/MySavedCards/index.tsx | 22 +++---- .../EnterDetails/Payment/PaymentClient.tsx | 24 +++---- .../Payment/PaymentOption/index.tsx | 63 +++++++++---------- .../PaymentOption/paymentOption.module.css | 10 ++- .../Payment/PaymentOption/paymentOption.ts | 5 -- .../Payment/PaymentOptionsGroup/index.tsx | 49 +++++++++++++++ .../Steps/ConfirmationStep/index.tsx | 43 ++++++------- .../MyStay/GuaranteeLateArrival/index.tsx | 32 +++++----- 9 files changed, 163 insertions(+), 111 deletions(-) create mode 100644 apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentOptionsGroup/index.tsx diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Confirm/index.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/Confirm/index.tsx index be6daeb6b..9897f8cd8 100644 --- a/apps/scandic-web/components/HotelReservation/EnterDetails/Confirm/index.tsx +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Confirm/index.tsx @@ -17,6 +17,7 @@ import { trackPaymentSectionOpen } from "@/utils/tracking/booking" import MySavedCards from "../Payment/MySavedCards" import PaymentOption from "../Payment/PaymentOption" +import PaymentOptionsGroup from "../Payment/PaymentOptionsGroup" import TermsAndConditions from "../Payment/TermsAndConditions" import styles from "./confirm.module.css" @@ -123,26 +124,25 @@ export default function ConfirmBooking({ {savedCreditCards?.length && guarantee ? ( ) : null} - {guarantee && ( - <> - {savedCreditCards?.length ? ( - -

- {intl.formatMessage({ + {guarantee ? ( + - - ) : null} + }) + : undefined + } + > - - )} + + ) : null}
diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/MySavedCards/index.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/MySavedCards/index.tsx index 1879535c7..a90330637 100644 --- a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/MySavedCards/index.tsx +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/MySavedCards/index.tsx @@ -1,13 +1,12 @@ import { useIntl } from "react-intl" -import { Typography } from "@scandic-hotels/design-system/Typography" - import { PAYMENT_METHOD_TITLES, type PaymentMethodEnum, } from "@/constants/booking" import PaymentOption from "../PaymentOption" +import PaymentOptionsGroup from "../PaymentOptionsGroup" import styles from "./mySavedCards.module.css" @@ -19,21 +18,20 @@ interface MySavedCardsProps { export default function MySavedCards({ savedCreditCards }: MySavedCardsProps) { const intl = useIntl() + const mySavedCardsLabel = intl.formatMessage({ + defaultMessage: "MY SAVED CARDS", + }) return (
- -

- {intl.formatMessage({ - defaultMessage: "MY SAVED CARDS", - })} -

-
-
+ {savedCreditCards?.map((savedCreditCard) => ( ))} -
+
) } diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentClient.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentClient.tsx index 45b9d09d9..7ab4f71fa 100644 --- a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentClient.tsx +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentClient.tsx @@ -43,6 +43,7 @@ import MixedRatePaymentBreakdown from "./MixedRatePaymentBreakdown" import MySavedCards from "./MySavedCards" import PaymentAlert from "./PaymentAlert" import PaymentOption from "./PaymentOption" +import PaymentOptionsGroup from "./PaymentOptionsGroup" import { type PaymentFormData, paymentSchema } from "./schema" import TermsAndConditions from "./TermsAndConditions" @@ -496,16 +497,18 @@ export default function PaymentClient({ ) : null}
- {savedCreditCards?.length ? ( - - {intl.formatMessage({ - defaultMessage: "OTHER PAYMENT METHODS", - })} - - ) : null} -
+ ( ))} -
+ {hasMixedRates ? ( -
- trackUpdatePaymentMethod("", value)} - {...register(name, registerOptions)} - /> - - {label} -
- {cardNumber ? ( + + cx(styles.paymentOption, { [styles.focused]: isFocusVisible }) + } + > + {({ isSelected }) => ( <> - {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */} - •••• {cardNumber} +
+ + {label} +
+ {cardNumber ? ( + <> + {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */} + •••• {cardNumber} + + ) : ( + {label} + )} - ) : ( - {label} )} - +
) } diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentOption/paymentOption.module.css b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentOption/paymentOption.module.css index 9d9e72248..765658cf4 100644 --- a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentOption/paymentOption.module.css +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentOption/paymentOption.module.css @@ -11,7 +11,12 @@ cursor: pointer; } -.paymentOption .radio { +.paymentOption.focused { + outline: 2px solid var(--UI-Input-Controls-Border-Focus); + outline-offset: 2px; +} + +.radio { width: 24px; height: 24px; border: 1px solid var(--Base-Border-Normal); @@ -19,7 +24,7 @@ cursor: pointer; } -.paymentOption input:checked + .radio { +.radio.selected { border: 8px solid var(--UI-Input-Controls-Fill-Selected); } @@ -27,7 +32,6 @@ display: flex; align-items: center; gap: var(--Spacing-x-one-and-half); - pointer-events: none; } .paymentOptionIcon { diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentOption/paymentOption.ts b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentOption/paymentOption.ts index e72e7f98b..2bb3e3ab6 100644 --- a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentOption/paymentOption.ts +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentOption/paymentOption.ts @@ -1,10 +1,5 @@ -import type { RegisterOptions } from "react-hook-form" - export interface PaymentOptionProps { - name: string value: string label: string cardNumber?: string - registerOptions?: RegisterOptions - onChange?: () => void } diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentOptionsGroup/index.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentOptionsGroup/index.tsx new file mode 100644 index 000000000..2d4b868d9 --- /dev/null +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentOptionsGroup/index.tsx @@ -0,0 +1,49 @@ +"use client" + +import { Label, RadioGroup } from "react-aria-components" +import { useController, useFormContext } from "react-hook-form" + +import { Typography } from "@scandic-hotels/design-system/Typography" + +import { trackUpdatePaymentMethod } from "@/utils/tracking" + +import type { ReactNode } from "react" + +interface PaymentOptionsGroupProps { + name: string + label?: string + children: ReactNode + className?: string +} + +export default function PaymentOptionsGroup({ + name, + label, + children, + className, +}: PaymentOptionsGroupProps) { + const { control } = useFormContext() + + const { + field: { value, onChange }, + } = useController({ + name, + control, + }) + + const handleChange = (newValue: string) => { + onChange(newValue) + trackUpdatePaymentMethod("", newValue) + } + + return ( + + {label ? ( + + + + ) : null} + {children} + + ) +} diff --git a/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddAncillaryFlow/AddAncillaryFlowModal/Steps/ConfirmationStep/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddAncillaryFlow/AddAncillaryFlowModal/Steps/ConfirmationStep/index.tsx index f68df6323..7eebb833d 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddAncillaryFlow/AddAncillaryFlowModal/Steps/ConfirmationStep/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddAncillaryFlow/AddAncillaryFlowModal/Steps/ConfirmationStep/index.tsx @@ -14,6 +14,7 @@ import { useAddAncillaryStore } from "@/stores/my-stay/add-ancillary-flow" import MySavedCards from "@/components/HotelReservation/EnterDetails/Payment/MySavedCards" import PaymentOption from "@/components/HotelReservation/EnterDetails/Payment/PaymentOption" +import PaymentOptionsGroup from "@/components/HotelReservation/EnterDetails/Payment/PaymentOptionsGroup" import Alert from "@/components/TempDesignSystem/Alert" import Checkbox from "@/components/TempDesignSystem/Form/Checkbox" import Link from "@/components/TempDesignSystem/Link" @@ -117,14 +118,15 @@ export default function ConfirmationStep({

{guaranteeInfo ? ( - + + + ) : ( <> - {savedCreditCards?.length && ( + {savedCreditCards?.length ? ( - )} - <> - {savedCreditCards?.length && ( - -

- {intl.formatMessage({ + ) : null} + - - )} + }) + : undefined + } + > - + )} diff --git a/apps/scandic-web/components/HotelReservation/MyStay/GuaranteeLateArrival/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/GuaranteeLateArrival/index.tsx index 4af6627fb..bd37a9f77 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/GuaranteeLateArrival/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/GuaranteeLateArrival/index.tsx @@ -32,6 +32,7 @@ import { trackGlaSaveCardAttempt } from "@/utils/tracking/myStay" import MySavedCards from "../../EnterDetails/Payment/MySavedCards" import PaymentOption from "../../EnterDetails/Payment/PaymentOption" +import PaymentOptionsGroup from "../../EnterDetails/Payment/PaymentOptionsGroup" import { type GuaranteeFormData, paymentSchema } from "./schema" import styles from "./guaranteeLateArrival.module.css" @@ -135,22 +136,25 @@ export default function GuaranteeLateArrival({ })} {savedCreditCards?.length ? ( - <> - - - {intl.formatMessage({ - defaultMessage: "OTHER", - })} - - + ) : null} - + label={ + savedCreditCards?.length + ? intl.formatMessage({ + defaultMessage: "OTHER", + }) + : undefined + } + > + +