Skip to content

Commit 33dee96

Browse files
committed
fix(a11y): make the plan and billing-period choices keyboard-operable
Two shapes of the same defect in checkout. The plan cards already had a "Select Monthly" / "Select Yearly" button, but it carried no handler — the click lived on the Paper around it, which is mouse-only. Pressing Enter on the focused button did nothing. The button is the natural control, so it now holds the handler and the card stays a mouse convenience. The billing-period cards in StaticCheckoutModal have no inner button at all, so there the card really is the control and takes the semantics to match, via a helper beside the style one it already used.
1 parent 978a569 commit 33dee96

3 files changed

Lines changed: 34 additions & 6 deletions

File tree

frontend/editor/src/proprietary/components/shared/config/configSections/plan/StaticCheckoutModal.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ import { useTranslation } from "react-i18next";
1515
import LocalIcon from "@app/components/shared/LocalIcon";
1616
import { EmailStage } from "@app/components/shared/stripeCheckout/stages/EmailStage";
1717
import { validateEmail } from "@app/components/shared/stripeCheckout/utils/checkoutUtils";
18-
import { getClickablePaperStyle } from "@app/components/shared/stripeCheckout/utils/cardStyles";
18+
import {
19+
getClickableCardProps,
20+
getClickablePaperStyle,
21+
} from "@app/components/shared/stripeCheckout/utils/cardStyles";
1922
import {
2023
STATIC_STRIPE_LINKS,
2124
buildStripeUrlWithEmail,
@@ -194,7 +197,9 @@ const StaticCheckoutModal: React.FC<StaticCheckoutModalProps> = ({
194197
p="xl"
195198
radius="md"
196199
style={getClickablePaperStyle()}
197-
onClick={() => handlePeriodSelect("monthly")}
200+
{...getClickableCardProps(() =>
201+
handlePeriodSelect("monthly"),
202+
)}
198203
>
199204
<Stack
200205
gap="md"
@@ -218,7 +223,7 @@ const StaticCheckoutModal: React.FC<StaticCheckoutModalProps> = ({
218223
p="xl"
219224
radius="md"
220225
style={getClickablePaperStyle()}
221-
onClick={() => handlePeriodSelect("yearly")}
226+
{...getClickableCardProps(() => handlePeriodSelect("yearly"))}
222227
>
223228
<Stack
224229
gap="md"

frontend/editor/src/proprietary/components/shared/stripeCheckout/stages/PlanSelectionStage.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,11 @@ export const PlanSelectionStage: React.FC<PlanSelectionStageProps> = ({
8080
)}
8181

8282
<div style={{ marginTop: "auto", paddingTop: "1rem" }}>
83-
<Button variant="secondary" fullWidth>
83+
<Button
84+
variant="secondary"
85+
fullWidth
86+
onClick={() => onSelectPlan("monthly")}
87+
>
8488
{t("payment.planStage.selectMonthly", "Select Monthly")}
8589
</Button>
8690
</div>
@@ -193,7 +197,7 @@ export const PlanSelectionStage: React.FC<PlanSelectionStageProps> = ({
193197
)}
194198

195199
<div style={{ marginTop: "auto", paddingTop: "1rem" }}>
196-
<Button fullWidth>
200+
<Button fullWidth onClick={() => onSelectPlan("yearly")}>
197201
{t("payment.planStage.selectYearly", "Select Yearly")}
198202
</Button>
199203
</div>

frontend/editor/src/proprietary/components/shared/stripeCheckout/utils/cardStyles.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { CSSProperties } from "react";
1+
import { CSSProperties, KeyboardEvent } from "react";
22

33
/**
44
* Shared styling utilities for plan cards
@@ -46,3 +46,22 @@ export function getClickablePaperStyle(
4646
...getCardBorderStyle(isHighlighted),
4747
};
4848
}
49+
50+
/**
51+
* Semantics for a card that is itself the control. `getClickablePaperStyle`
52+
* only makes a card *look* clickable; without these a Paper with an onClick is
53+
* a div, so the choice cannot be reached or made by keyboard.
54+
*/
55+
export function getClickableCardProps(onActivate: () => void) {
56+
return {
57+
role: "button",
58+
tabIndex: 0,
59+
onClick: onActivate,
60+
onKeyDown: (e: KeyboardEvent<HTMLElement>) => {
61+
if (e.key === "Enter" || e.key === " ") {
62+
e.preventDefault();
63+
onActivate();
64+
}
65+
},
66+
};
67+
}

0 commit comments

Comments
 (0)