From a7223f98b99053819218a90133ab458c805b430c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 6 Aug 2025 21:18:19 +0000 Subject: [PATCH 1/4] Add buttonLabel option to customize action button text in Bridge UI Co-authored-by: joaquim.verges --- .../web/ui/Bridge/BridgeOrchestrator.tsx | 1 + .../src/react/web/ui/Bridge/BuyWidget.tsx | 8 ++++ .../react/web/ui/Bridge/CheckoutWidget.tsx | 6 +++ .../src/react/web/ui/Bridge/DirectPayment.tsx | 6 ++- .../src/react/web/ui/Bridge/FundWallet.tsx | 4 +- .../web/ui/Bridge/TransactionPayment.tsx | 2 +- .../react/web/ui/Bridge/TransactionWidget.tsx | 6 +++ .../stories/Bridge/DirectPayment.stories.tsx | 30 ++++++++++++++ .../src/stories/Bridge/FundWallet.stories.tsx | 30 ++++++++++++++ .../Bridge/TransactionPayment.stories.tsx | 30 ++++++++++++++ .../thirdweb/src/stories/Bridge/fixtures.ts | 40 +++++++++++++++++-- 11 files changed, 156 insertions(+), 7 deletions(-) diff --git a/packages/thirdweb/src/react/web/ui/Bridge/BridgeOrchestrator.tsx b/packages/thirdweb/src/react/web/ui/Bridge/BridgeOrchestrator.tsx index bc1540d9b88..baa04fc6195 100644 --- a/packages/thirdweb/src/react/web/ui/Bridge/BridgeOrchestrator.tsx +++ b/packages/thirdweb/src/react/web/ui/Bridge/BridgeOrchestrator.tsx @@ -42,6 +42,7 @@ export type UIOptions = Prettify< image?: string; }; currency?: SupportedFiatCurrency; + buttonLabel?: string; } & ( | { mode: "fund_wallet"; diff --git a/packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx b/packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx index c4048a7cac7..85f3e4266b7 100644 --- a/packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx +++ b/packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx @@ -180,6 +180,11 @@ export type BuyWidgetProps = { * @default "USD" */ currency?: SupportedFiatCurrency; + + /** + * Custom label for the main action button. + */ + buttonLabel?: string; }; // Enhanced UIOptions to handle unsupported token state @@ -335,6 +340,7 @@ export function BuyWidget(props: BuyWidgetProps) { }, mode: "fund_wallet", currency: props.currency || "USD", + buttonLabel: props.buttonLabel, }, type: "success", }; @@ -364,6 +370,8 @@ export function BuyWidget(props: BuyWidgetProps) { title: props.title, }, mode: "fund_wallet", + currency: props.currency || "USD", + buttonLabel: props.buttonLabel, }, type: "success", }; diff --git a/packages/thirdweb/src/react/web/ui/Bridge/CheckoutWidget.tsx b/packages/thirdweb/src/react/web/ui/Bridge/CheckoutWidget.tsx index 201790770a7..78a3d40cf1a 100644 --- a/packages/thirdweb/src/react/web/ui/Bridge/CheckoutWidget.tsx +++ b/packages/thirdweb/src/react/web/ui/Bridge/CheckoutWidget.tsx @@ -186,6 +186,11 @@ export type CheckoutWidgetProps = { * @default "USD" */ currency?: SupportedFiatCurrency; + + /** + * Custom label for the main action button. + */ + buttonLabel?: string; }; // Enhanced UIOptions to handle unsupported token state @@ -310,6 +315,7 @@ export function CheckoutWidget(props: CheckoutWidgetProps) { }, mode: "direct_payment", currency: props.currency || "USD", + buttonLabel: props.buttonLabel, paymentInfo: { amount: props.amount, feePayer: props.feePayer === "seller" ? "receiver" : "sender", diff --git a/packages/thirdweb/src/react/web/ui/Bridge/DirectPayment.tsx b/packages/thirdweb/src/react/web/ui/Bridge/DirectPayment.tsx index a8be79d332a..bda8674ab35 100644 --- a/packages/thirdweb/src/react/web/ui/Bridge/DirectPayment.tsx +++ b/packages/thirdweb/src/react/web/ui/Bridge/DirectPayment.tsx @@ -64,7 +64,11 @@ export function DirectPayment({ ); }; - const buyNow = ( + const buyNow = uiOptions.buttonLabel ? ( + + {uiOptions.buttonLabel} + + ) : ( Buy Now ยท diff --git a/packages/thirdweb/src/react/web/ui/Bridge/FundWallet.tsx b/packages/thirdweb/src/react/web/ui/Bridge/FundWallet.tsx index 664a792c0ff..cf7d6f622b9 100644 --- a/packages/thirdweb/src/react/web/ui/Bridge/FundWallet.tsx +++ b/packages/thirdweb/src/react/web/ui/Bridge/FundWallet.tsx @@ -336,13 +336,13 @@ export function FundWallet({ }} variant="primary" > - Buy {amount} {uiOptions.destinationToken.symbol} + {uiOptions.buttonLabel || `Buy ${amount} ${uiOptions.destinationToken.symbol}`} ) : ( ; // UI Options for FundWallet mode export const FUND_WALLET_UI_OPTIONS: Record< - "ethDefault" | "ethWithAmount" | "usdcDefault" | "uniLarge", + "ethDefault" | "ethWithAmount" | "usdcDefault" | "uniLarge" | "customButton", FundWalletUIOptions > = { ethDefault: { @@ -718,11 +718,21 @@ export const FUND_WALLET_UI_OPTIONS: Record< initialAmount: "5", mode: "fund_wallet" as const, }, + customButton: { + destinationToken: ETH, + initialAmount: "0.01", + metadata: { + description: "Test custom button label for funding", + title: "Custom Fund Wallet", + }, + mode: "fund_wallet" as const, + buttonLabel: "Add Funds Now", + }, }; // UI Options for DirectPayment mode export const DIRECT_PAYMENT_UI_OPTIONS: Record< - "digitalArt" | "concertTicket" | "subscription" | "sneakers" | "credits", + "digitalArt" | "concertTicket" | "subscription" | "sneakers" | "credits" | "customButton", DirectPaymentUIOptions > = { concertTicket: { @@ -794,11 +804,26 @@ export const DIRECT_PAYMENT_UI_OPTIONS: Record< token: USDC, }, }, + customButton: { + metadata: { + description: "Test custom button label functionality", + image: PRODUCT_METADATA.digitalArt.image, + title: "Custom Button Test", + }, + mode: "direct_payment" as const, + buttonLabel: "Purchase Now", + paymentInfo: { + amount: "0.05", + feePayer: "sender" as const, + sellerAddress: RECEIVER_ADDRESSES.primary, + token: ETH, + }, + }, }; // UI Options for Transaction mode export const TRANSACTION_UI_OPTIONS: Record< - "ethTransfer" | "erc20Transfer" | "contractInteraction", + "ethTransfer" | "erc20Transfer" | "contractInteraction" | "customButton", TransactionUIOptions > = { contractInteraction: { @@ -825,4 +850,13 @@ export const TRANSACTION_UI_OPTIONS: Record< mode: "transaction" as const, transaction: ethTransferTransaction, }, + customButton: { + metadata: { + description: "Test custom button label for transactions", + title: "Custom Transaction", + }, + mode: "transaction" as const, + buttonLabel: "Execute Now", + transaction: ethTransferTransaction, + }, }; From a61b402c519e787c688d20cd479bcc7f9ce94f60 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 6 Aug 2025 21:34:02 +0000 Subject: [PATCH 2/4] Add optional button label customization to payment components Co-authored-by: joaquim.verges --- .../src/app/payments/components/CodeGen.tsx | 2 +- .../src/app/payments/components/types.ts | 1 + .../src/app/payments/embed/LeftSection.tsx | 21 +++++++++++++++++++ .../src/app/payments/embed/RightSection.tsx | 3 +++ .../src/app/payments/embed/page.tsx | 1 + 5 files changed, 27 insertions(+), 1 deletion(-) diff --git a/apps/playground-web/src/app/payments/components/CodeGen.tsx b/apps/playground-web/src/app/payments/components/CodeGen.tsx index e4e61de457f..70f46c55afc 100644 --- a/apps/playground-web/src/app/payments/components/CodeGen.tsx +++ b/apps/playground-web/src/app/payments/components/CodeGen.tsx @@ -71,7 +71,7 @@ function Example() { <${componentName} client={client} chain={defineChain(${options.payOptions.buyTokenChain.id})} - amount="${options.payOptions.buyTokenAmount}"${options.payOptions.buyTokenAddress ? `\n\t tokenAddress="${options.payOptions.buyTokenAddress}"` : ""}${options.payOptions.sellerAddress ? `\n\t seller="${options.payOptions.sellerAddress}"` : ""}${options.payOptions.title ? `\n\t ${options.payOptions.widget === "checkout" ? "name" : "title"}="${options.payOptions.title}"` : ""}${options.payOptions.image ? `\n\t image="${options.payOptions.image}"` : ""}${options.payOptions.description ? `\n\t description="${options.payOptions.description}"` : ""}${options.payOptions.paymentMethods && options.payOptions.paymentMethods.length > 0 ? `\n\t paymentMethods={${JSON.stringify(options.payOptions.paymentMethods)}}` : ""}${options.payOptions.currency ? `\n\t currency="${options.payOptions.currency}"` : ""}${ + amount="${options.payOptions.buyTokenAmount}"${options.payOptions.buyTokenAddress ? `\n\t tokenAddress="${options.payOptions.buyTokenAddress}"` : ""}${options.payOptions.sellerAddress ? `\n\t seller="${options.payOptions.sellerAddress}"` : ""}${options.payOptions.title ? `\n\t ${options.payOptions.widget === "checkout" ? "name" : "title"}="${options.payOptions.title}"` : ""}${options.payOptions.image ? `\n\t image="${options.payOptions.image}"` : ""}${options.payOptions.description ? `\n\t description="${options.payOptions.description}"` : ""}${options.payOptions.buttonLabel ? `\n\t buttonLabel="${options.payOptions.buttonLabel}"` : ""}${options.payOptions.paymentMethods && options.payOptions.paymentMethods.length > 0 ? `\n\t paymentMethods={${JSON.stringify(options.payOptions.paymentMethods)}}` : ""}${options.payOptions.currency ? `\n\t currency="${options.payOptions.currency}"` : ""}${ options.payOptions.widget === "transaction" ? `\n\t transaction={claimTo({ contract: nftContract, diff --git a/apps/playground-web/src/app/payments/components/types.ts b/apps/playground-web/src/app/payments/components/types.ts index f6713f34fed..c509a255042 100644 --- a/apps/playground-web/src/app/payments/components/types.ts +++ b/apps/playground-web/src/app/payments/components/types.ts @@ -41,6 +41,7 @@ export type BridgeComponentsPlaygroundOptions = { title: string | undefined; image: string | undefined; description: string | undefined; + buttonLabel: string | undefined; buyTokenAddress?: Address; buyTokenAmount: string; diff --git a/apps/playground-web/src/app/payments/embed/LeftSection.tsx b/apps/playground-web/src/app/payments/embed/LeftSection.tsx index 91c034830d8..90a22d0dd1f 100644 --- a/apps/playground-web/src/app/payments/embed/LeftSection.tsx +++ b/apps/playground-web/src/app/payments/embed/LeftSection.tsx @@ -73,6 +73,7 @@ export function LeftSection(props: { const modalTitleId = useId(); const modalTitleIconId = useId(); const modalDescriptionId = useId(); + const buttonLabelId = useId(); const themeId = useId(); const cryptoPaymentId = useId(); const cardPaymentId = useId(); @@ -486,6 +487,26 @@ export function LeftSection(props: { value={options.payOptions.description} /> + + {/* Button Label */} +
+ + + setOptions((v) => ({ + ...v, + payOptions: { + ...payOptions, + buttonLabel: e.target.value, + }, + })) + } + placeholder="Custom button text (optional)" + value={options.payOptions.buttonLabel} + /> +
diff --git a/apps/playground-web/src/app/payments/embed/RightSection.tsx b/apps/playground-web/src/app/payments/embed/RightSection.tsx index 483868bc8ab..1cea186b2e8 100644 --- a/apps/playground-web/src/app/payments/embed/RightSection.tsx +++ b/apps/playground-web/src/app/payments/embed/RightSection.tsx @@ -66,6 +66,7 @@ export function RightSection(props: { tokenAddress={props.options.payOptions.buyTokenAddress} currency={props.options.payOptions.currency} showThirdwebBranding={props.options.payOptions.showThirdwebBranding} + {...(props.options.payOptions.buttonLabel && { buttonLabel: props.options.payOptions.buttonLabel })} /> ); } @@ -91,6 +92,7 @@ export function RightSection(props: { tokenAddress={props.options.payOptions.buyTokenAddress} currency={props.options.payOptions.currency} showThirdwebBranding={props.options.payOptions.showThirdwebBranding} + {...(props.options.payOptions.buttonLabel && { buttonLabel: props.options.payOptions.buttonLabel })} /> ); } @@ -112,6 +114,7 @@ export function RightSection(props: { })} currency={props.options.payOptions.currency} showThirdwebBranding={props.options.payOptions.showThirdwebBranding} + {...(props.options.payOptions.buttonLabel && { buttonLabel: props.options.payOptions.buttonLabel })} /> ); } diff --git a/apps/playground-web/src/app/payments/embed/page.tsx b/apps/playground-web/src/app/payments/embed/page.tsx index 07e4e6ae3d7..085d86a4eb3 100644 --- a/apps/playground-web/src/app/payments/embed/page.tsx +++ b/apps/playground-web/src/app/payments/embed/page.tsx @@ -13,6 +13,7 @@ const defaultConnectOptions: BridgeComponentsPlaygroundOptions = { buyTokenChain: arbitrum, description: "", image: "", + buttonLabel: "", paymentMethods: ["crypto", "card"], sellerAddress: "0x0000000000000000000000000000000000000000", title: "", From 0e2c5d06eb0b02961393265da56e9b76d8f1c81d Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Thu, 7 Aug 2025 13:16:34 +1200 Subject: [PATCH 3/4] lint --- .../src/app/payments/embed/RightSection.tsx | 12 +++++++++--- .../thirdweb/src/react/web/ui/Bridge/FundWallet.tsx | 7 +++++-- .../src/stories/Bridge/DirectPayment.stories.tsx | 3 ++- .../src/stories/Bridge/FundWallet.stories.tsx | 3 ++- .../stories/Bridge/TransactionPayment.stories.tsx | 3 ++- packages/thirdweb/src/stories/Bridge/fixtures.ts | 7 ++++++- 6 files changed, 26 insertions(+), 9 deletions(-) diff --git a/apps/playground-web/src/app/payments/embed/RightSection.tsx b/apps/playground-web/src/app/payments/embed/RightSection.tsx index 1cea186b2e8..b7b6a5052ea 100644 --- a/apps/playground-web/src/app/payments/embed/RightSection.tsx +++ b/apps/playground-web/src/app/payments/embed/RightSection.tsx @@ -66,7 +66,9 @@ export function RightSection(props: { tokenAddress={props.options.payOptions.buyTokenAddress} currency={props.options.payOptions.currency} showThirdwebBranding={props.options.payOptions.showThirdwebBranding} - {...(props.options.payOptions.buttonLabel && { buttonLabel: props.options.payOptions.buttonLabel })} + {...(props.options.payOptions.buttonLabel && { + buttonLabel: props.options.payOptions.buttonLabel, + })} /> ); } @@ -92,7 +94,9 @@ export function RightSection(props: { tokenAddress={props.options.payOptions.buyTokenAddress} currency={props.options.payOptions.currency} showThirdwebBranding={props.options.payOptions.showThirdwebBranding} - {...(props.options.payOptions.buttonLabel && { buttonLabel: props.options.payOptions.buttonLabel })} + {...(props.options.payOptions.buttonLabel && { + buttonLabel: props.options.payOptions.buttonLabel, + })} /> ); } @@ -114,7 +118,9 @@ export function RightSection(props: { })} currency={props.options.payOptions.currency} showThirdwebBranding={props.options.payOptions.showThirdwebBranding} - {...(props.options.payOptions.buttonLabel && { buttonLabel: props.options.payOptions.buttonLabel })} + {...(props.options.payOptions.buttonLabel && { + buttonLabel: props.options.payOptions.buttonLabel, + })} /> ); } diff --git a/packages/thirdweb/src/react/web/ui/Bridge/FundWallet.tsx b/packages/thirdweb/src/react/web/ui/Bridge/FundWallet.tsx index cf7d6f622b9..cced6742343 100644 --- a/packages/thirdweb/src/react/web/ui/Bridge/FundWallet.tsx +++ b/packages/thirdweb/src/react/web/ui/Bridge/FundWallet.tsx @@ -336,13 +336,16 @@ export function FundWallet({ }} variant="primary" > - {uiOptions.buttonLabel || `Buy ${amount} ${uiOptions.destinationToken.symbol}`} + {uiOptions.buttonLabel || + `Buy ${amount} ${uiOptions.destinationToken.symbol}`} ) : ( = { concertTicket: { From 001326c6e8a935dd6fed027d703015aa95d00da9 Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Thu, 7 Aug 2025 13:17:11 +1200 Subject: [PATCH 4/4] changeset --- .changeset/upset-pears-unite.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/upset-pears-unite.md diff --git a/.changeset/upset-pears-unite.md b/.changeset/upset-pears-unite.md new file mode 100644 index 00000000000..d696c3997eb --- /dev/null +++ b/.changeset/upset-pears-unite.md @@ -0,0 +1,5 @@ +--- +"thirdweb": patch +--- + +Add ability to override button label for all payment widgets