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", () => { it("orders the default channel first and renders page analytics", () => {
const html = renderToStaticMarkup( const html = renderToStaticMarkup(
<PaymentMethodSelector <PaymentMethodSelector
config={{
canChoosePaymentMethod: true,
initialPayChannel: "ezpay",
}}
value="ezpay" value="ezpay"
defaultChannel="ezpay"
caption="GCash by default" caption="GCash by default"
analyticsKey="tip.payment_method" analyticsKey="tip.payment_method"
onChange={() => undefined} onChange={() => undefined}
@@ -40,6 +43,10 @@ describe("PaymentMethodSelector", () => {
it("disables both payment methods while payment is busy", () => { it("disables both payment methods while payment is busy", () => {
const html = renderToStaticMarkup( const html = renderToStaticMarkup(
<PaymentMethodSelector <PaymentMethodSelector
config={{
canChoosePaymentMethod: true,
initialPayChannel: "stripe",
}}
value="stripe" value="stripe"
disabled disabled
analyticsKey="subscription.payment_method" analyticsKey="subscription.payment_method"
@@ -52,4 +59,20 @@ describe("PaymentMethodSelector", () => {
'data-analytics-key="subscription.payment_method"', '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 Image from "next/image";
import type { PayChannel } from "@/data/schemas/payment"; import type { PayChannel } from "@/data/schemas/payment";
import type { PaymentMethodConfig } from "@/lib/payment/payment_method";
const PAYMENT_METHODS: readonly { const PAYMENT_METHODS: readonly {
channel: PayChannel; channel: PayChannel;
@@ -21,24 +22,28 @@ const PAYMENT_METHODS: readonly {
]; ];
export interface PaymentMethodSelectorProps { export interface PaymentMethodSelectorProps {
config: PaymentMethodConfig;
value: PayChannel; value: PayChannel;
defaultChannel?: PayChannel;
disabled?: boolean; disabled?: boolean;
caption?: string; caption?: string;
className?: string;
analyticsKey: string; analyticsKey: string;
onChange: (channel: PayChannel) => void; onChange: (channel: PayChannel) => void;
} }
export function PaymentMethodSelector({ export function PaymentMethodSelector({
config,
value, value,
defaultChannel = "stripe",
disabled = false, disabled = false,
caption = "Stripe by default", caption = "Stripe by default",
className,
analyticsKey, analyticsKey,
onChange, onChange,
}: PaymentMethodSelectorProps) { }: PaymentMethodSelectorProps) {
if (!config.canChoosePaymentMethod) return null;
const paymentMethods = const paymentMethods =
defaultChannel === "ezpay" config.initialPayChannel === "ezpay"
? [...PAYMENT_METHODS].sort((a, b) => ? [...PAYMENT_METHODS].sort((a, b) =>
a.channel === "ezpay" ? -1 : b.channel === "ezpay" ? 1 : 0, a.channel === "ezpay" ? -1 : b.channel === "ezpay" ? 1 : 0,
) )
@@ -46,7 +51,7 @@ export function PaymentMethodSelector({
return ( return (
<section <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" aria-labelledby="payment-method-title"
> >
<div className="flex items-baseline justify-between gap-sm px-xs"> <div className="flex items-baseline justify-between gap-sm px-xs">
+9 -12
View File
@@ -224,18 +224,15 @@ export function SubscriptionScreen({
/> />
</div> </div>
{paymentMethodConfig.canChoosePaymentMethod ? ( <PaymentMethodSelector
<section className={styles.paymentMethodSlot}> config={paymentMethodConfig}
<PaymentMethodSelector value={payment.payChannel}
value={payment.payChannel} disabled={isPaymentBusy}
defaultChannel={paymentMethodConfig.initialPayChannel} caption="GCash by default in the Philippines"
disabled={isPaymentBusy} className={styles.paymentMethodSlot}
caption="GCash by default in the Philippines" analyticsKey="subscription.payment_method"
analyticsKey="subscription.payment_method" onChange={handlePaymentMethodChange}
onChange={handlePaymentMethodChange} />
/>
</section>
) : null}
<div className={styles.ctaSlot}> <div className={styles.ctaSlot}>
<SubscriptionCheckoutButton <SubscriptionCheckoutButton
+9 -12
View File
@@ -284,18 +284,15 @@ export function TipScreen({
/> />
</section> </section>
{paymentMethodConfig.canChoosePaymentMethod ? ( <PaymentMethodSelector
<section className={styles.paymentMethodSlot}> config={paymentMethodConfig}
<PaymentMethodSelector value={payment.payChannel}
value={payment.payChannel} disabled={isPaymentBusy}
defaultChannel={paymentMethodConfig.initialPayChannel} caption="GCash by default in the Philippines"
disabled={isPaymentBusy} className={styles.paymentMethodSlot}
caption="GCash by default in the Philippines" analyticsKey="tip.payment_method"
analyticsKey="tip.payment_method" onChange={handlePaymentMethodChange}
onChange={handlePaymentMethodChange} />
/>
</section>
) : null}
{showMissingPlan ? ( {showMissingPlan ? (
<p className={styles.statusMessage} role="alert"> <p className={styles.statusMessage} role="alert">