From fddb791f0af6fee3dabcbaa05fa1c3aa97f34177 Mon Sep 17 00:00:00 2001 From: MananTank Date: Fri, 19 Sep 2025 10:12:08 +0000 Subject: [PATCH] Playground: Fix Pay components incomplete code gen, cleanup (#8078) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ## PR-Codex overview This PR primarily focuses on refactoring the payment components in the `playground-web` application, updating widget properties and enhancing code generation functions for better modularity and readability. ### Detailed summary - Changed `lockedWidget` to `widget` in multiple components: `CheckoutPlayground`, `TransactionPlayground`, `BuyPlayground`, `LeftSection`, and `RightSection`. - Added new functions in `code-gen.ts` for `stringifyProps`, `quotes`, `stringifyImports`, and `stringifyIgnoreFalsy`. - Updated `CodeGen` components to accept `widget` as a prop. - Refactored `getCode` function in `CodeGen` to use the new `widget` parameter. - Removed unused imports and functions from `CodeGen` and `LeftSection`. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` ## Summary by CodeRabbit - New Features - Code generator now supports explicit widget selection (buy, checkout, transaction) with unified, props-driven output. - Enhanced theming in generated snippets, including dark/light themes with overrides. - Refactor - Migrated playground code generation to centralized utilities for imports and props. - Simplified widget configuration by removing it from options and passing it as a direct prop. - Streamlined Payments UI; removed “Sponsor gas fees” section. - Style - Improved code snippet formatting with wider line wrap for cleaner display. --- .../bridge/swap-widget/components/code.tsx | 34 +--- .../payments/commerce/CheckoutPlayground.tsx | 5 +- .../src/app/payments/components/CodeGen.tsx | 146 ++++++++++++------ .../app/payments/components/LeftSection.tsx | 40 +---- .../app/payments/components/RightSection.tsx | 14 +- .../src/app/payments/components/types.ts | 1 - .../payments/fund-wallet/BuyPlayground.tsx | 9 +- .../transactions/TransactionPlayground.tsx | 5 +- .../wallets/sign-in/components/CodeGen.tsx | 62 ++------ apps/playground-web/src/lib/code-gen.ts | 43 ++++++ .../ui/src/components/code/getCodeHtml.tsx | 2 +- 11 files changed, 183 insertions(+), 178 deletions(-) create mode 100644 apps/playground-web/src/lib/code-gen.ts diff --git a/apps/playground-web/src/app/bridge/swap-widget/components/code.tsx b/apps/playground-web/src/app/bridge/swap-widget/components/code.tsx index b0a2358dbbe..9e76d15b36f 100644 --- a/apps/playground-web/src/app/bridge/swap-widget/components/code.tsx +++ b/apps/playground-web/src/app/bridge/swap-widget/components/code.tsx @@ -1,5 +1,6 @@ import { lazy, Suspense } from "react"; import { LoadingDots } from "@/components/ui/LoadingDots"; +import { quotes, stringifyImports, stringifyProps } from "@/lib/code-gen"; import type { SwapWidgetPlaygroundOptions } from "./types"; const CodeClient = lazy(() => @@ -28,7 +29,8 @@ export function CodeGen(props: { options: SwapWidgetPlaygroundOptions }) { function getCode(options: SwapWidgetPlaygroundOptions) { const imports = { - react: ["SwapWidget"] as string[], + thirdweb: ["createThirdwebClient"] as string[], + "thirdweb/react": ["SwapWidget"] as string[], }; let themeProp: string | undefined; @@ -39,7 +41,7 @@ function getCode(options: SwapWidgetPlaygroundOptions) { themeProp = `darkTheme({ colors: ${JSON.stringify(options.theme.darkColorOverrides)}, })`; - imports.react.push("darkTheme"); + imports["thirdweb/react"].push("darkTheme"); } if (options.theme.type === "light") { @@ -47,7 +49,7 @@ function getCode(options: SwapWidgetPlaygroundOptions) { themeProp = `lightTheme({ colors: ${JSON.stringify(options.theme.lightColorOverrides)}, })`; - imports.react.push("lightTheme"); + imports["thirdweb/react"].push("lightTheme"); } else { themeProp = quotes("light"); } @@ -69,37 +71,15 @@ function getCode(options: SwapWidgetPlaygroundOptions) { }; return `\ -import { createThirdwebClient } from "thirdweb"; -import { ${imports.react.join(", ")} } from "thirdweb/react"; +${stringifyImports(imports)} const client = createThirdwebClient({ clientId: "....", }); - function Example() { return ( - + ); }`; } - -function quotes(value: string) { - return `"${value}"`; -} - -function stringifyProps(props: Record) { - const _props: Record = {}; - - for (const key in props) { - if (props[key] !== undefined && props[key] !== "") { - _props[key] = props[key]; - } - } - - return Object.entries(_props) - .map(([key, value]) => `${key}={${value}}`) - .join("\n\t "); -} diff --git a/apps/playground-web/src/app/payments/commerce/CheckoutPlayground.tsx b/apps/playground-web/src/app/payments/commerce/CheckoutPlayground.tsx index 8df4071b824..555df2a526f 100644 --- a/apps/playground-web/src/app/payments/commerce/CheckoutPlayground.tsx +++ b/apps/playground-web/src/app/payments/commerce/CheckoutPlayground.tsx @@ -18,7 +18,6 @@ const defaultOptions: BridgeComponentsPlaygroundOptions = { sellerAddress: "0x0000000000000000000000000000000000000000", title: "", transactionData: "", - widget: "checkout", currency: "USD", showThirdwebBranding: true, }, @@ -37,12 +36,12 @@ export function CheckoutPlayground() {
- +
); } diff --git a/apps/playground-web/src/app/payments/components/CodeGen.tsx b/apps/playground-web/src/app/payments/components/CodeGen.tsx index 2cca950d3bc..aaa1b15ef51 100644 --- a/apps/playground-web/src/app/payments/components/CodeGen.tsx +++ b/apps/playground-web/src/app/payments/components/CodeGen.tsx @@ -1,5 +1,10 @@ import { lazy, Suspense } from "react"; import { LoadingDots } from "@/components/ui/LoadingDots"; +import { + quotes, + stringifyImports, + stringifyProps, +} from "../../../lib/code-gen"; import type { BridgeComponentsPlaygroundOptions } from "./types"; const CodeClient = lazy(() => @@ -16,58 +21,114 @@ function CodeLoading() { ); } -export function CodeGen(props: { options: BridgeComponentsPlaygroundOptions }) { +export function CodeGen(props: { + widget: "buy" | "checkout" | "transaction"; + options: BridgeComponentsPlaygroundOptions; +}) { return (
}> - +
); } -function getCode(options: BridgeComponentsPlaygroundOptions) { +function getCode( + widget: "buy" | "checkout" | "transaction", + options: BridgeComponentsPlaygroundOptions, +) { + const componentName = + widget === "buy" + ? "BuyWidget" + : widget === "checkout" + ? "CheckoutWidget" + : widget === "transaction" + ? "TransactionWidget" + : ""; + const imports = { - chains: [] as string[], - react: ["PayEmbed"] as string[], - thirdweb: [] as string[], - wallets: [] as string[], + "thirdweb/chains": [] as string[], + "thirdweb/react": [componentName] as string[], + thirdweb: ["createThirdwebClient", "defineChain"] as string[], + "thirdweb/wallets": ["createWallet"] as string[], }; - // Check if we have a custom chain (not base chain which has id 8453) - const isCustomChain = - options.payOptions.buyTokenChain && - options.payOptions.buyTokenChain.id !== 8453; + let themeProp: string | undefined; + if ( + options.theme.type === "dark" && + Object.keys(options.theme.darkColorOverrides || {}).length > 0 + ) { + themeProp = `darkTheme({ + colors: ${JSON.stringify(options.theme.darkColorOverrides)}, + })`; + imports["thirdweb/react"].push("darkTheme"); + } - if (isCustomChain) { - // Add defineChain to imports if using a custom chain - imports.thirdweb.push("defineChain"); - } else { - // Otherwise use the base chain - imports.chains.push("base"); + if (options.theme.type === "light") { + if (Object.keys(options.theme.lightColorOverrides || {}).length > 0) { + themeProp = `lightTheme({ + colors: ${JSON.stringify(options.theme.lightColorOverrides)}, + })`; + imports["thirdweb/react"].push("lightTheme"); + } else { + themeProp = quotes("light"); + } } - imports.wallets.push("createWallet"); + const transaction = + widget === "transaction" + ? `claimTo({ +contract: nftContract, +quantity: 1n, +to: account?.address || "", +tokenId: 2n, +})` + : undefined; - const componentName = (() => { - switch (options.payOptions.widget) { - case "buy": - return "BuyWidget"; - case "checkout": - return "CheckoutWidget"; - case "transaction": - return "TransactionWidget"; - default: - return "PayEmbed"; - } - })(); - imports.react.push(componentName); - imports.chains.push("defineChain"); + const props: Record = { + theme: themeProp, + client: "client", + description: options.payOptions.description + ? quotes(options.payOptions.description) + : undefined, + image: options.payOptions.image + ? quotes(options.payOptions.image) + : undefined, + name: options.payOptions.title + ? quotes(options.payOptions.title) + : undefined, + paymentMethods: + options.payOptions.paymentMethods.length === 2 + ? undefined + : JSON.stringify(options.payOptions.paymentMethods), + currency: options.payOptions.currency + ? quotes(options.payOptions.currency) + : undefined, + chain: `defineChain(${options.payOptions.buyTokenChain.id})`, + showThirdwebBranding: + options.payOptions.showThirdwebBranding === false ? false : undefined, + amount: options.payOptions.buyTokenAmount + ? quotes(options.payOptions.buyTokenAmount) + : undefined, + tokenAddress: options.payOptions.buyTokenAddress + ? quotes(options.payOptions.buyTokenAddress) + : undefined, + seller: options.payOptions.sellerAddress + ? quotes(options.payOptions.sellerAddress) + : undefined, + buttonLabel: options.payOptions.buttonLabel + ? quotes(options.payOptions.buttonLabel) + : undefined, + transaction: transaction, + }; return `\ -import { createThirdwebClient } from "thirdweb"; -${imports.react.length > 0 ? `import { ${imports.react.join(", ")} } from "thirdweb/react";` : ""} -${imports.thirdweb.length > 0 ? `import { ${imports.thirdweb.join(", ")} } from "thirdweb";` : ""} +${stringifyImports(imports)} const client = createThirdwebClient({ clientId: "....", @@ -75,20 +136,7 @@ const client = createThirdwebClient({ function Example() { return ( - <${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.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, - quantity: 1n, - tokenId: 2n, - to: account?.address || "", - })}` - : "" - } - /> + <${componentName} ${stringifyProps(props)} /> ); }`; } diff --git a/apps/playground-web/src/app/payments/components/LeftSection.tsx b/apps/playground-web/src/app/payments/components/LeftSection.tsx index f72a84ff13b..6685c50e4cc 100644 --- a/apps/playground-web/src/app/payments/components/LeftSection.tsx +++ b/apps/playground-web/src/app/payments/components/LeftSection.tsx @@ -1,13 +1,6 @@ "use client"; -import { - CreditCardIcon, - ExternalLinkIcon, - FuelIcon, - PaletteIcon, - Settings2Icon, -} from "lucide-react"; -import Link from "next/link"; +import { CreditCardIcon, PaletteIcon, Settings2Icon } from "lucide-react"; import type React from "react"; import { useId, useState } from "react"; import type { Address } from "thirdweb"; @@ -30,11 +23,10 @@ export function LeftSection(props: { setOptions: React.Dispatch< React.SetStateAction >; - lockedWidget: "buy" | "checkout" | "transaction"; + widget: "buy" | "checkout" | "transaction"; }) { const { options, setOptions } = props; const { theme, payOptions } = options; - const effectiveWidget = props.lockedWidget || payOptions.widget || "buy"; const setThemeType = (themeType: "dark" | "light") => { setOptions((v) => ({ ...v, @@ -130,7 +122,7 @@ export function LeftSection(props: { {/* Shared Chain and Token Selection - Always visible for Buy and Checkout modes */} - {(effectiveWidget === "buy" || effectiveWidget === "checkout") && ( + {(props.widget === "buy" || props.widget === "checkout") && (
{/* Chain selection */}
@@ -164,7 +156,7 @@ export function LeftSection(props: { {/* Mode-specific form fields */}
{/* Buy Mode - Amount and Payment Methods */} - {effectiveWidget === "buy" && ( + {props.widget === "buy" && (
@@ -245,7 +237,7 @@ export function LeftSection(props: { )} {/* Checkout Mode - Seller Address, Price and Payment Methods */} - {effectiveWidget === "checkout" && ( + {props.widget === "checkout" && (
@@ -347,7 +339,7 @@ export function LeftSection(props: { )} {/* Transaction Mode Options */} - {effectiveWidget === "transaction" && ( + {props.widget === "transaction" && (
@@ -495,26 +487,6 @@ export function LeftSection(props: {
- - -
-
-

- Abstract away gas fees for users of your app by enabling ERC-4337 - Account Abstraction -

- - - Learn more about Account Abstraction - - -
-
-
); } diff --git a/apps/playground-web/src/app/payments/components/RightSection.tsx b/apps/playground-web/src/app/payments/components/RightSection.tsx index 36b76a8a215..3285ac4a5bc 100644 --- a/apps/playground-web/src/app/payments/components/RightSection.tsx +++ b/apps/playground-web/src/app/payments/components/RightSection.tsx @@ -29,7 +29,7 @@ type Tab = "ui" | "code"; export function RightSection(props: { options: BridgeComponentsPlaygroundOptions; tab?: string; - lockedWidget?: "buy" | "checkout" | "transaction"; + widget: "buy" | "checkout" | "transaction"; }) { const pathname = usePathname(); const [previewTab, _setPreviewTab] = useState(() => { @@ -52,10 +52,8 @@ export function RightSection(props: { colors: props.options.theme.lightColorOverrides, }); - const effectiveWidget = props.lockedWidget || props.options.payOptions.widget; - let embed: React.ReactNode; - if (effectiveWidget === "buy") { + if (props.widget === "buy") { embed = ( } + {previewTab === "code" && ( + + )}
); diff --git a/apps/playground-web/src/app/payments/components/types.ts b/apps/playground-web/src/app/payments/components/types.ts index 6b8f6b57137..aef999010a8 100644 --- a/apps/playground-web/src/app/payments/components/types.ts +++ b/apps/playground-web/src/app/payments/components/types.ts @@ -37,7 +37,6 @@ export type BridgeComponentsPlaygroundOptions = { lightColorOverrides: ThemeOverrides["colors"]; }; payOptions: { - widget?: "buy" | "checkout" | "transaction"; title: string | undefined; image: string | undefined; description: string | undefined; diff --git a/apps/playground-web/src/app/payments/fund-wallet/BuyPlayground.tsx b/apps/playground-web/src/app/payments/fund-wallet/BuyPlayground.tsx index c96863a39f5..a942c9d074b 100644 --- a/apps/playground-web/src/app/payments/fund-wallet/BuyPlayground.tsx +++ b/apps/playground-web/src/app/payments/fund-wallet/BuyPlayground.tsx @@ -18,7 +18,6 @@ const defaultOptions: BridgeComponentsPlaygroundOptions = { sellerAddress: "0x0000000000000000000000000000000000000000", title: "", transactionData: "", - widget: "buy", currency: "USD", showThirdwebBranding: true, }, @@ -36,13 +35,9 @@ export function BuyPlayground() { return (
- +
- +
); } diff --git a/apps/playground-web/src/app/payments/transactions/TransactionPlayground.tsx b/apps/playground-web/src/app/payments/transactions/TransactionPlayground.tsx index 4b0a9959f39..1b598964ea2 100644 --- a/apps/playground-web/src/app/payments/transactions/TransactionPlayground.tsx +++ b/apps/playground-web/src/app/payments/transactions/TransactionPlayground.tsx @@ -18,7 +18,6 @@ const defaultOptions: BridgeComponentsPlaygroundOptions = { sellerAddress: "0x0000000000000000000000000000000000000000", title: "", transactionData: "", - widget: "transaction", currency: "USD", showThirdwebBranding: true, }, @@ -37,12 +36,12 @@ export function TransactionPlayground() {
- +
); } diff --git a/apps/playground-web/src/app/wallets/sign-in/components/CodeGen.tsx b/apps/playground-web/src/app/wallets/sign-in/components/CodeGen.tsx index 408b14d8ce0..58ee78ad0d0 100644 --- a/apps/playground-web/src/app/wallets/sign-in/components/CodeGen.tsx +++ b/apps/playground-web/src/app/wallets/sign-in/components/CodeGen.tsx @@ -1,4 +1,10 @@ import { lazy, Suspense } from "react"; +import { + quotes, + stringifyIgnoreFalsy, + stringifyImports, + stringifyProps, +} from "@/lib/code-gen"; import { LoadingDots } from "../../../../components/ui/LoadingDots"; import type { ConnectPlaygroundOptions } from "./types"; @@ -34,10 +40,10 @@ export function CodeGen(props: { connectOptions: ConnectPlaygroundOptions }) { function getCode(connectOptions: ConnectPlaygroundOptions) { const walletCodes: string[] = []; const imports = { - chains: [] as string[], - react: [] as string[], - thirdweb: [] as string[], - wallets: [] as string[], + "thirdweb/chains": [] as string[], + "thirdweb/react": ["ConnectButton"] as string[], + thirdweb: ["createThirdwebClient"] as string[], + "thirdweb/wallets": [] as string[], }; if ( @@ -49,7 +55,7 @@ function getCode(connectOptions: ConnectPlaygroundOptions) { options: ${JSON.stringify(connectOptions.inAppWallet.methods)}, }, })`); - imports.wallets.push("inAppWallet"); + imports["thirdweb/wallets"].push("inAppWallet"); } for (const wallet of connectOptions.walletIds) { @@ -57,7 +63,7 @@ function getCode(connectOptions: ConnectPlaygroundOptions) { } if (connectOptions.walletIds.length > 0) { - imports.wallets.push("createWallet"); + imports["thirdweb/wallets"].push("createWallet"); } let themeProp: string | undefined; @@ -68,7 +74,7 @@ function getCode(connectOptions: ConnectPlaygroundOptions) { themeProp = `darkTheme({ colors: ${JSON.stringify(connectOptions.theme.darkColorOverrides)}, })`; - imports.react.push("darkTheme"); + imports["thirdweb/react"].push("darkTheme"); } if (connectOptions.theme.type === "light") { @@ -78,14 +84,14 @@ function getCode(connectOptions: ConnectPlaygroundOptions) { themeProp = `lightTheme({ colors: ${JSON.stringify(connectOptions.theme.lightColorOverrides)}, })`; - imports.react.push("lightTheme"); + imports["thirdweb/react"].push("lightTheme"); } else { themeProp = quotes("light"); } } if (connectOptions.enableAccountAbstraction) { - imports.chains.push("ethereum"); + imports["thirdweb/chains"].push("ethereum"); } const props: Record = { @@ -118,11 +124,7 @@ function getCode(connectOptions: ConnectPlaygroundOptions) { }; return `\ -import { createThirdwebClient } from "thirdweb"; -import { ConnectButton } from "thirdweb/react"; -${imports.react.length > 0 ? `import { ${imports.react.join(", ")} } from "thirdweb/react";` : ""} -${imports.wallets.length > 0 ? `import { ${imports.wallets.join(", ")} } from "thirdweb/wallets";` : ""} -${imports.chains.length > 0 ? `import { ${imports.chains.join(", ")} } from "thirdweb/chains";` : ""} +${stringifyImports(imports)} const client = createThirdwebClient({ clientId: "....", @@ -160,35 +162,3 @@ const accountAbstractCode = `\ sponsorGas: true } `; - -function stringifyIgnoreFalsy( - value: Record, -) { - const _value: Record = {}; - - for (const key in value) { - if (value[key] !== undefined && value[key] !== "") { - _value[key] = value[key]; - } - } - - return JSON.stringify(_value); -} - -function stringifyProps(props: Record) { - const _props: Record = {}; - - for (const key in props) { - if (props[key] !== undefined && props[key] !== "") { - _props[key] = props[key]; - } - } - - return Object.entries(_props) - .map(([key, value]) => `${key}={${value}}`) - .join("\n"); -} - -function quotes(value: string) { - return `"${value}"`; -} diff --git a/apps/playground-web/src/lib/code-gen.ts b/apps/playground-web/src/lib/code-gen.ts new file mode 100644 index 00000000000..16df909fd32 --- /dev/null +++ b/apps/playground-web/src/lib/code-gen.ts @@ -0,0 +1,43 @@ +export function stringifyProps( + props: Record, +) { + const _props: Record = {}; + + for (const key in props) { + if (props[key] !== undefined && props[key] !== "") { + _props[key] = props[key]; + } + } + + return Object.entries(_props) + .map(([key, value]) => `${key}={${value}}`) + .join("\n"); +} + +export function quotes(value: string) { + return `"${value}"`; +} + +export function stringifyImports(imports: Record) { + let code = ""; + for (const key in imports) { + if (imports[key].length > 0) { + code += `import { ${imports[key].join(", ")} } from "${key}";\n`; + } + } + return code; +} + +export function stringifyIgnoreFalsy( + value: Record, +) { + const _value: Record = {}; + + for (const key in value) { + if (value[key] !== undefined && value[key] !== "") { + _value[key] = value[key]; + } + } + + return JSON.stringify(_value); +} diff --git a/packages/ui/src/components/code/getCodeHtml.tsx b/packages/ui/src/components/code/getCodeHtml.tsx index 768e1e2934e..0283309b333 100644 --- a/packages/ui/src/components/code/getCodeHtml.tsx +++ b/packages/ui/src/components/code/getCodeHtml.tsx @@ -25,7 +25,7 @@ export async function getCodeHtml( ? await format(code, { parser: "typescript", plugins: [estreePlugin, typescriptPlugin], - printWidth: 60, + printWidth: 80, }).catch((e) => { if (!options?.ignoreFormattingErrors) { console.error(e);