Skip to content

Commit

Permalink
WIP: fix(payments-next): Implement a11y improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
xlisachan committed Jun 3, 2024
1 parent 915fcd5 commit 3035ef8
Show file tree
Hide file tree
Showing 23 changed files with 619 additions and 241 deletions.
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { revalidatePath } from 'next/cache';
import { headers } from 'next/headers';
import { PaymentSection } from '@fxa/payments/ui';
import {
app,
getCartOrRedirectAction,
SupportedPages,
} from '@fxa/payments/ui/server';
import { DEFAULT_LOCALE } from '@fxa/shared/l10n';
import { auth, signIn } from 'apps/payments/next/auth';
import { headers } from 'next/headers';
import { CheckoutParams } from '../layout';
import {
getFakeCartData,
getContentfulContent,
} from 'apps/payments/next/app/_lib/apiClient';
import { PaymentSection } from '@fxa/payments/ui';
import { auth, signIn } from 'apps/payments/next/auth';
import { PrimaryButton } from 'libs/payments/ui/src/lib/client/components/PrimaryButton';
import { revalidatePath } from 'next/cache';
import { CheckoutParams } from '../layout';

export const dynamic = 'force-dynamic';

Expand Down Expand Up @@ -62,21 +63,21 @@ export default async function Checkout({ params }: { params: CheckoutParams }) {
await signIn('fxa');
}}
>
<p className="text-grey-400 text-sm mt-2 mb-4">
<p className="text-grey-400 text-sm mt-2 pb-4 row-divider-grey-200">
{l10n.getFragmentWithSource(
'next-new-user-sign-in-link-2',
{
elems: {
a: (
<button className="underline text-grey-400 hover:text-grey-400">
<button className="underline hover:text-grey-400">
Sign in
</button>
),
},
},
<>
Already have a Mozilla account?&nbsp;
<button className="underline text-grey-400 hover:text-grey-400">
<button className="underline hover:text-grey-400">
Sign in
</button>
</>
Expand Down Expand Up @@ -115,14 +116,12 @@ export default async function Checkout({ params }: { params: CheckoutParams }) {
<PrimaryButton type="submit"> Set email</PrimaryButton>
</form>
</div>

<hr className="mx-auto w-full border-grey-200" />
</>
)}

{!session ? (
<h2
className="font-semibold text-grey-600 text-lg mt-14 mb-5"
className="font-semibold text-grey-600 text-lg mt-10 mb-5"
data-testid="header-prefix"
>
{l10n.getString(
Expand All @@ -132,7 +131,7 @@ export default async function Checkout({ params }: { params: CheckoutParams }) {
</h2>
) : (
<h2
className="font-semibold text-grey-600 text-lg mt-14 mb-5"
className="font-semibold text-grey-600 text-lg mt-10 mb-5"
data-testid="header"
>
{l10n.getString(
Expand All @@ -141,7 +140,7 @@ export default async function Checkout({ params }: { params: CheckoutParams }) {
)}
</h2>
)}
<h3 className="font-semibold my-3 text-grey-600 text-start">
<h3 className="font-semibold text-grey-600 text-start">
{l10n.getString(
'next-payment-method-first-approve',
`First you'll need to approve your subscription`
Expand Down
2 changes: 1 addition & 1 deletion apps/payments/next/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default function RootLayout({
<html lang="en">
<body>
<header
className="bg-white fixed flex justify-between items-center shadow h-16 left-0 top-0 mx-auto my-0 px-4 py-0 w-full z-10 tablet:h-20"
className="bg-white fixed flex justify-between items-center shadow h-16 left-0 top-0 mx-auto my-0 px-4 py-0 w-full z-40 tablet:h-20"
role="banner"
data-testid="header"
>
Expand Down
150 changes: 77 additions & 73 deletions libs/payments/ui/src/lib/client/components/CheckoutCheckbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use client';

import * as HoverCard from '@radix-ui/react-hover-card';
import { useEffect, useState } from 'react';
import { Localized } from '@fluent/react';
import * as Tooltip from '@radix-ui/react-tooltip';
import { useEffect, useState } from 'react';
import { LinkExternal } from '@fxa/shared/react/components/LinkExternal';

interface CheckoutCheckboxProps {
isRequired: boolean;
Expand Down Expand Up @@ -38,84 +39,87 @@ export function CheckoutCheckbox({
};

return (
<HoverCard.Root open={isRequired && !isChecked}>
<label className="flex gap-5 items-center mt-6">
<HoverCard.Trigger>
<input
type="checkbox"
name="confirm"
className="grow-0 shrink-0 basis-4 scale-150 cursor-pointer"
checked={isChecked}
onChange={changeHandler}
/>
</HoverCard.Trigger>
{isClient && (
<Localized
id="next-payment-confirm-with-legal-links-static-3"
elems={{
termsOfServiceLink: (
<a
<Tooltip.Provider>
<Tooltip.Root open={isRequired && !isChecked}>
<label className="flex gap-5 items-center my-6">
<Tooltip.Trigger asChild>
<input
type="checkbox"
name="confirm"
className="grow-0 shrink-0 basis-4 scale-150 cursor-pointer"
checked={isChecked}
onChange={changeHandler}
required
aria-describedby="checkboxError"
aria-required
/>
</Tooltip.Trigger>
{isClient && (
<Localized
id="next-payment-confirm-with-legal-links-static-3"
elems={{
termsOfServiceLink: (
<LinkExternal
href={termsOfService}
className="text-blue-500 underline"
>
Terms of Service
</LinkExternal>
),
privacyNoticeLink: (
<LinkExternal
href={privacyNotice}
className="text-blue-500 underline"
>
Privacy Notice
</LinkExternal>
),
}}
>
<span className="font-normal text-sm leading-5 block">
I authorize Mozilla to charge my payment method for the amount
shown, according to{' '}
<LinkExternal
href={termsOfService}
target="_blank"
rel="noreferrer"
className="text-blue-500 underline"
>
Terms of Service
</a>
),
privacyNoticeLink: (
<a
</LinkExternal>{' '}
and{' '}
<LinkExternal
href={privacyNotice}
target="_blank"
rel="noreferrer"
className="text-blue-500 underline"
>
Privacy Notice
</a>
),
}}
>
<span className="font-normal text-sm block">
I authorize Mozilla to charge my payment method for the amount
shown, according to{' '}
<a
href={termsOfService}
target="_blank"
rel="noreferrer"
className="text-blue-500 underline"
>
Terms of Service
</a>{' '}
and{' '}
<a
href={privacyNotice}
target="_blank"
rel="noreferrer"
className="text-blue-500 underline"
>
Privacy Notice
</a>
, until I cancel my subscription.
</span>
</Localized>
)}
</label>
<HoverCard.Portal>
<HoverCard.Content
className="animate-slide-up z-20"
sideOffset={20}
align="start"
alignOffset={50}
arrowPadding={20}
>
<Localized id="next-payment-confirm-checkbox-error">
<div className="text-white text-sm bg-alert-red py-1.5 px-4">
You need to complete this before moving forward
</div>
</Localized>
<HoverCard.Arrow className="fill-alert-red" height={11} width={22} />
</HoverCard.Content>
</HoverCard.Portal>
</HoverCard.Root>
</LinkExternal>
, until I cancel my subscription.
</span>
</Localized>
)}
<Tooltip.Portal>
<Tooltip.Content
id="checkboxError"
className="animate-slide-up z-20"
sideOffset={20}
align="start"
alignOffset={50}
arrowPadding={20}
role="alert"
>
<Localized id="next-payment-confirm-checkbox-error">
<div className="text-white text-sm bg-alert-red py-1.5 px-4">
You need to complete this before moving forward
</div>
</Localized>
<Tooltip.Arrow
className="fill-alert-red"
height={11}
width={22}
/>
</Tooltip.Content>
</Tooltip.Portal>
</label>
</Tooltip.Root>
</Tooltip.Provider>
);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
## Checkout Form

next-new-user-submit = Subscribe Now
next-payment-validate-name-error = Please enter your full name
# Label for the Full Name input
payment-name-label = Name as it appears on your card
payment-name-placeholder = Full Name
32 changes: 19 additions & 13 deletions libs/payments/ui/src/lib/client/components/CheckoutForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use client';

import { Localized, useLocalization } from '@fluent/react';
import * as Form from '@radix-ui/react-form';
import {
PaymentElement,
useStripe,
useElements,
} from '@stripe/react-stripe-js';
import { StripePaymentElementChangeEvent } from '@stripe/stripe-js';
import { checkoutCartWithStripe } from '../../actions/checkoutCartWithStripe';
import { useEffect, useState } from 'react';
import { handleStripeErrorAction } from '../../actions/handleStripeError';
import LockImage from '@fxa/shared/assets/images/lock.svg';
import Image from 'next/image';
import { useRouter } from 'next/navigation';
import * as Form from '@radix-ui/react-form';
import { Localized, useLocalization } from '@fluent/react';
import { useEffect, useState } from 'react';
import LockImage from '@fxa/shared/assets/images/lock.svg';
import { PrimaryButton } from './PrimaryButton';
import { checkoutCartWithStripe } from '../../actions/checkoutCartWithStripe';
import { handleStripeErrorAction } from '../../actions/handleStripeError';

interface CheckoutFormProps {
readOnly: boolean;
Expand Down Expand Up @@ -135,10 +135,10 @@ export function CheckoutForm({ readOnly, cart, locale }: CheckoutFormProps) {
const nonStripeFieldsComplete = !!fullName;

return (
<Form.Root className="flex flex-col gap-4" onSubmit={submitHandler}>
<Form.Root onSubmit={submitHandler}>
{!isPaymentElementLoading && (
<Form.Field name="name" serverInvalid={hasFullNameError}>
<Form.Label className="font-medium text-sm text-grey-400 block mb-1 text-start">
<Form.Label className="text-[15px] text-grey-400 block mb-1 text-start">
<Localized id="payment-name-label">
Name as it appears on your card
</Localized>
Expand All @@ -153,19 +153,24 @@ export function CheckoutForm({ readOnly, cart, locale }: CheckoutFormProps) {
{},
'Full Name'
)}
readOnly={readOnly}
readOnly
tabIndex={readOnly ? -1 : 0}
value={fullName}
onChange={(e) => {
setFullName(e.target.value);
setHasFullNameError(!e.target.value);
}}
required
aria-required
/>
</Form.Control>
{hasFullNameError && (
<Form.Message asChild>
<p className="text-sm mt-1 text-alert-red">
Please enter your name
</p>
<Form.Message asChild role="alert">
<Localized id="next-payment-validate-name-error">
<p className="text-[15px] mt-1 text-alert-red">
Please enter your name
</p>
</Localized>
</Form.Message>
)}
</Form.Field>
Expand All @@ -189,6 +194,7 @@ export function CheckoutForm({ readOnly, cart, locale }: CheckoutFormProps) {
aria-disabled={
!stripeFieldsComplete || !nonStripeFieldsComplete || loading
}
tabIndex={readOnly ? -1 : 0}
>
<Image
src={LockImage}
Expand Down
Loading

0 comments on commit 3035ef8

Please sign in to comment.