refactor(payment): unify method selector visibility

This commit is contained in:
2026-07-20 16:14:48 +08:00
parent bc6373f4c0
commit 1f8bbe9022
4 changed files with 51 additions and 29 deletions
@@ -7,8 +7,11 @@ describe("PaymentMethodSelector", () => {
it("orders the default channel first and renders page analytics", () => {
const html = renderToStaticMarkup(
<PaymentMethodSelector
config={{
canChoosePaymentMethod: true,
initialPayChannel: "ezpay",
}}
value="ezpay"
defaultChannel="ezpay"
caption="GCash by default"
analyticsKey="tip.payment_method"
onChange={() => undefined}
@@ -40,6 +43,10 @@ describe("PaymentMethodSelector", () => {
it("disables both payment methods while payment is busy", () => {
const html = renderToStaticMarkup(
<PaymentMethodSelector
config={{
canChoosePaymentMethod: true,
initialPayChannel: "stripe",
}}
value="stripe"
disabled
analyticsKey="subscription.payment_method"
@@ -52,4 +59,20 @@ describe("PaymentMethodSelector", () => {
'data-analytics-key="subscription.payment_method"',
);
});
it("does not render when payment method selection is unavailable", () => {
const html = renderToStaticMarkup(
<PaymentMethodSelector
config={{
canChoosePaymentMethod: false,
initialPayChannel: "stripe",
}}
value="stripe"
analyticsKey="tip.payment_method"
onChange={() => undefined}
/>,
);
expect(html).toBe("");
});
});
@@ -3,6 +3,7 @@
import Image from "next/image";
import type { PayChannel } from "@/data/schemas/payment";
import type { PaymentMethodConfig } from "@/lib/payment/payment_method";
const PAYMENT_METHODS: readonly {
channel: PayChannel;
@@ -21,24 +22,28 @@ const PAYMENT_METHODS: readonly {
];
export interface PaymentMethodSelectorProps {
config: PaymentMethodConfig;
value: PayChannel;
defaultChannel?: PayChannel;
disabled?: boolean;
caption?: string;
className?: string;
analyticsKey: string;
onChange: (channel: PayChannel) => void;
}
export function PaymentMethodSelector({
config,
value,
defaultChannel = "stripe",
disabled = false,
caption = "Stripe by default",
className,
analyticsKey,
onChange,
}: PaymentMethodSelectorProps) {
if (!config.canChoosePaymentMethod) return null;
const paymentMethods =
defaultChannel === "ezpay"
config.initialPayChannel === "ezpay"
? [...PAYMENT_METHODS].sort((a, b) =>
a.channel === "ezpay" ? -1 : b.channel === "ezpay" ? 1 : 0,
)
@@ -46,7 +51,7 @@ export function PaymentMethodSelector({
return (
<section
className="flex flex-col gap-[clamp(8px,1.852vw,10px)]"
className={`flex flex-col gap-[clamp(8px,1.852vw,10px)] ${className ?? ""}`.trim()}
aria-labelledby="payment-method-title"
>
<div className="flex items-baseline justify-between gap-sm px-xs">
+2 -5
View File
@@ -224,18 +224,15 @@ export function SubscriptionScreen({
/>
</div>
{paymentMethodConfig.canChoosePaymentMethod ? (
<section className={styles.paymentMethodSlot}>
<PaymentMethodSelector
config={paymentMethodConfig}
value={payment.payChannel}
defaultChannel={paymentMethodConfig.initialPayChannel}
disabled={isPaymentBusy}
caption="GCash by default in the Philippines"
className={styles.paymentMethodSlot}
analyticsKey="subscription.payment_method"
onChange={handlePaymentMethodChange}
/>
</section>
) : null}
<div className={styles.ctaSlot}>
<SubscriptionCheckoutButton
+2 -5
View File
@@ -284,18 +284,15 @@ export function TipScreen({
/>
</section>
{paymentMethodConfig.canChoosePaymentMethod ? (
<section className={styles.paymentMethodSlot}>
<PaymentMethodSelector
config={paymentMethodConfig}
value={payment.payChannel}
defaultChannel={paymentMethodConfig.initialPayChannel}
disabled={isPaymentBusy}
caption="GCash by default in the Philippines"
className={styles.paymentMethodSlot}
analyticsKey="tip.payment_method"
onChange={handlePaymentMethodChange}
/>
</section>
) : null}
{showMissingPlan ? (
<p className={styles.statusMessage} role="alert">