Skip to content

Commit fddb791

Browse files
committed
Playground: Fix Pay components incomplete code gen, cleanup (#8078)
<!-- ## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes" If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000): ## Notes for the reviewer Anything important to call out? Be sure to also clarify these in your comments. ## How to test Unit tests, playground, etc. --> <!-- start pr-codex --> --- ## 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}` <!-- end pr-codex --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## 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. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 1732e63 commit fddb791

File tree

11 files changed

+183
-178
lines changed

11 files changed

+183
-178
lines changed

apps/playground-web/src/app/bridge/swap-widget/components/code.tsx

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { lazy, Suspense } from "react";
22
import { LoadingDots } from "@/components/ui/LoadingDots";
3+
import { quotes, stringifyImports, stringifyProps } from "@/lib/code-gen";
34
import type { SwapWidgetPlaygroundOptions } from "./types";
45

56
const CodeClient = lazy(() =>
@@ -28,7 +29,8 @@ export function CodeGen(props: { options: SwapWidgetPlaygroundOptions }) {
2829

2930
function getCode(options: SwapWidgetPlaygroundOptions) {
3031
const imports = {
31-
react: ["SwapWidget"] as string[],
32+
thirdweb: ["createThirdwebClient"] as string[],
33+
"thirdweb/react": ["SwapWidget"] as string[],
3234
};
3335

3436
let themeProp: string | undefined;
@@ -39,15 +41,15 @@ function getCode(options: SwapWidgetPlaygroundOptions) {
3941
themeProp = `darkTheme({
4042
colors: ${JSON.stringify(options.theme.darkColorOverrides)},
4143
})`;
42-
imports.react.push("darkTheme");
44+
imports["thirdweb/react"].push("darkTheme");
4345
}
4446

4547
if (options.theme.type === "light") {
4648
if (Object.keys(options.theme.lightColorOverrides || {}).length > 0) {
4749
themeProp = `lightTheme({
4850
colors: ${JSON.stringify(options.theme.lightColorOverrides)},
4951
})`;
50-
imports.react.push("lightTheme");
52+
imports["thirdweb/react"].push("lightTheme");
5153
} else {
5254
themeProp = quotes("light");
5355
}
@@ -69,37 +71,15 @@ function getCode(options: SwapWidgetPlaygroundOptions) {
6971
};
7072

7173
return `\
72-
import { createThirdwebClient } from "thirdweb";
73-
import { ${imports.react.join(", ")} } from "thirdweb/react";
74+
${stringifyImports(imports)}
7475
7576
const client = createThirdwebClient({
7677
clientId: "....",
7778
});
7879
79-
8080
function Example() {
8181
return (
82-
<SwapWidget
83-
${stringifyProps(props)}
84-
/>
82+
<SwapWidget ${stringifyProps(props)} />
8583
);
8684
}`;
8785
}
88-
89-
function quotes(value: string) {
90-
return `"${value}"`;
91-
}
92-
93-
function stringifyProps(props: Record<string, string | undefined | boolean>) {
94-
const _props: Record<string, string | undefined | boolean> = {};
95-
96-
for (const key in props) {
97-
if (props[key] !== undefined && props[key] !== "") {
98-
_props[key] = props[key];
99-
}
100-
}
101-
102-
return Object.entries(_props)
103-
.map(([key, value]) => `${key}={${value}}`)
104-
.join("\n\t ");
105-
}

apps/playground-web/src/app/payments/commerce/CheckoutPlayground.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ const defaultOptions: BridgeComponentsPlaygroundOptions = {
1818
sellerAddress: "0x0000000000000000000000000000000000000000",
1919
title: "",
2020
transactionData: "",
21-
widget: "checkout",
2221
currency: "USD",
2322
showThirdwebBranding: true,
2423
},
@@ -37,12 +36,12 @@ export function CheckoutPlayground() {
3736
<div className="relative flex flex-col-reverse gap-6 xl:min-h-[900px] xl:flex-row xl:gap-6">
3837
<div className="grow border-b pb-10 xl:mb-0 xl:border-r xl:border-b-0 xl:pr-6">
3938
<LeftSection
40-
lockedWidget="checkout"
39+
widget="checkout"
4140
options={options}
4241
setOptions={setOptions}
4342
/>
4443
</div>
45-
<RightSection lockedWidget="checkout" options={options} />
44+
<RightSection widget="checkout" options={options} />
4645
</div>
4746
);
4847
}
Lines changed: 97 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { lazy, Suspense } from "react";
22
import { LoadingDots } from "@/components/ui/LoadingDots";
3+
import {
4+
quotes,
5+
stringifyImports,
6+
stringifyProps,
7+
} from "../../../lib/code-gen";
38
import type { BridgeComponentsPlaygroundOptions } from "./types";
49

510
const CodeClient = lazy(() =>
@@ -16,79 +21,122 @@ function CodeLoading() {
1621
);
1722
}
1823

19-
export function CodeGen(props: { options: BridgeComponentsPlaygroundOptions }) {
24+
export function CodeGen(props: {
25+
widget: "buy" | "checkout" | "transaction";
26+
options: BridgeComponentsPlaygroundOptions;
27+
}) {
2028
return (
2129
<div className="flex w-full grow flex-col">
2230
<Suspense fallback={<CodeLoading />}>
23-
<CodeClient className="grow" code={getCode(props.options)} lang="tsx" />
31+
<CodeClient
32+
className="grow"
33+
code={getCode(props.widget, props.options)}
34+
lang="tsx"
35+
/>
2436
</Suspense>
2537
</div>
2638
);
2739
}
2840

29-
function getCode(options: BridgeComponentsPlaygroundOptions) {
41+
function getCode(
42+
widget: "buy" | "checkout" | "transaction",
43+
options: BridgeComponentsPlaygroundOptions,
44+
) {
45+
const componentName =
46+
widget === "buy"
47+
? "BuyWidget"
48+
: widget === "checkout"
49+
? "CheckoutWidget"
50+
: widget === "transaction"
51+
? "TransactionWidget"
52+
: "";
53+
3054
const imports = {
31-
chains: [] as string[],
32-
react: ["PayEmbed"] as string[],
33-
thirdweb: [] as string[],
34-
wallets: [] as string[],
55+
"thirdweb/chains": [] as string[],
56+
"thirdweb/react": [componentName] as string[],
57+
thirdweb: ["createThirdwebClient", "defineChain"] as string[],
58+
"thirdweb/wallets": ["createWallet"] as string[],
3559
};
3660

37-
// Check if we have a custom chain (not base chain which has id 8453)
38-
const isCustomChain =
39-
options.payOptions.buyTokenChain &&
40-
options.payOptions.buyTokenChain.id !== 8453;
61+
let themeProp: string | undefined;
62+
if (
63+
options.theme.type === "dark" &&
64+
Object.keys(options.theme.darkColorOverrides || {}).length > 0
65+
) {
66+
themeProp = `darkTheme({
67+
colors: ${JSON.stringify(options.theme.darkColorOverrides)},
68+
})`;
69+
imports["thirdweb/react"].push("darkTheme");
70+
}
4171

42-
if (isCustomChain) {
43-
// Add defineChain to imports if using a custom chain
44-
imports.thirdweb.push("defineChain");
45-
} else {
46-
// Otherwise use the base chain
47-
imports.chains.push("base");
72+
if (options.theme.type === "light") {
73+
if (Object.keys(options.theme.lightColorOverrides || {}).length > 0) {
74+
themeProp = `lightTheme({
75+
colors: ${JSON.stringify(options.theme.lightColorOverrides)},
76+
})`;
77+
imports["thirdweb/react"].push("lightTheme");
78+
} else {
79+
themeProp = quotes("light");
80+
}
4881
}
4982

50-
imports.wallets.push("createWallet");
83+
const transaction =
84+
widget === "transaction"
85+
? `claimTo({
86+
contract: nftContract,
87+
quantity: 1n,
88+
to: account?.address || "",
89+
tokenId: 2n,
90+
})`
91+
: undefined;
5192

52-
const componentName = (() => {
53-
switch (options.payOptions.widget) {
54-
case "buy":
55-
return "BuyWidget";
56-
case "checkout":
57-
return "CheckoutWidget";
58-
case "transaction":
59-
return "TransactionWidget";
60-
default:
61-
return "PayEmbed";
62-
}
63-
})();
64-
imports.react.push(componentName);
65-
imports.chains.push("defineChain");
93+
const props: Record<string, string | undefined | boolean> = {
94+
theme: themeProp,
95+
client: "client",
96+
description: options.payOptions.description
97+
? quotes(options.payOptions.description)
98+
: undefined,
99+
image: options.payOptions.image
100+
? quotes(options.payOptions.image)
101+
: undefined,
102+
name: options.payOptions.title
103+
? quotes(options.payOptions.title)
104+
: undefined,
105+
paymentMethods:
106+
options.payOptions.paymentMethods.length === 2
107+
? undefined
108+
: JSON.stringify(options.payOptions.paymentMethods),
109+
currency: options.payOptions.currency
110+
? quotes(options.payOptions.currency)
111+
: undefined,
112+
chain: `defineChain(${options.payOptions.buyTokenChain.id})`,
113+
showThirdwebBranding:
114+
options.payOptions.showThirdwebBranding === false ? false : undefined,
115+
amount: options.payOptions.buyTokenAmount
116+
? quotes(options.payOptions.buyTokenAmount)
117+
: undefined,
118+
tokenAddress: options.payOptions.buyTokenAddress
119+
? quotes(options.payOptions.buyTokenAddress)
120+
: undefined,
121+
seller: options.payOptions.sellerAddress
122+
? quotes(options.payOptions.sellerAddress)
123+
: undefined,
124+
buttonLabel: options.payOptions.buttonLabel
125+
? quotes(options.payOptions.buttonLabel)
126+
: undefined,
127+
transaction: transaction,
128+
};
66129

67130
return `\
68-
import { createThirdwebClient } from "thirdweb";
69-
${imports.react.length > 0 ? `import { ${imports.react.join(", ")} } from "thirdweb/react";` : ""}
70-
${imports.thirdweb.length > 0 ? `import { ${imports.thirdweb.join(", ")} } from "thirdweb";` : ""}
131+
${stringifyImports(imports)}
71132
72133
const client = createThirdwebClient({
73134
clientId: "....",
74135
});
75136
76137
function Example() {
77138
return (
78-
<${componentName}
79-
client={client}
80-
chain={defineChain(${options.payOptions.buyTokenChain.id})}
81-
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}"` : ""}${
82-
options.payOptions.widget === "transaction"
83-
? `\n\t transaction={claimTo({
84-
contract: nftContract,
85-
quantity: 1n,
86-
tokenId: 2n,
87-
to: account?.address || "",
88-
})}`
89-
: ""
90-
}
91-
/>
139+
<${componentName} ${stringifyProps(props)} />
92140
);
93141
}`;
94142
}

apps/playground-web/src/app/payments/components/LeftSection.tsx

Lines changed: 6 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
"use client";
22

3-
import {
4-
CreditCardIcon,
5-
ExternalLinkIcon,
6-
FuelIcon,
7-
PaletteIcon,
8-
Settings2Icon,
9-
} from "lucide-react";
10-
import Link from "next/link";
3+
import { CreditCardIcon, PaletteIcon, Settings2Icon } from "lucide-react";
114
import type React from "react";
125
import { useId, useState } from "react";
136
import type { Address } from "thirdweb";
@@ -30,11 +23,10 @@ export function LeftSection(props: {
3023
setOptions: React.Dispatch<
3124
React.SetStateAction<BridgeComponentsPlaygroundOptions>
3225
>;
33-
lockedWidget: "buy" | "checkout" | "transaction";
26+
widget: "buy" | "checkout" | "transaction";
3427
}) {
3528
const { options, setOptions } = props;
3629
const { theme, payOptions } = options;
37-
const effectiveWidget = props.lockedWidget || payOptions.widget || "buy";
3830
const setThemeType = (themeType: "dark" | "light") => {
3931
setOptions((v) => ({
4032
...v,
@@ -130,7 +122,7 @@ export function LeftSection(props: {
130122
</section>
131123

132124
{/* Shared Chain and Token Selection - Always visible for Buy and Checkout modes */}
133-
{(effectiveWidget === "buy" || effectiveWidget === "checkout") && (
125+
{(props.widget === "buy" || props.widget === "checkout") && (
134126
<div className="space-y-4">
135127
{/* Chain selection */}
136128
<div className="flex flex-col gap-2">
@@ -164,7 +156,7 @@ export function LeftSection(props: {
164156
{/* Mode-specific form fields */}
165157
<div className="my-2">
166158
{/* Buy Mode - Amount and Payment Methods */}
167-
{effectiveWidget === "buy" && (
159+
{props.widget === "buy" && (
168160
<div className="space-y-4">
169161
<div className="flex flex-col gap-2">
170162
<Label htmlFor={buyTokenAmountId}>Amount</Label>
@@ -245,7 +237,7 @@ export function LeftSection(props: {
245237
)}
246238

247239
{/* Checkout Mode - Seller Address, Price and Payment Methods */}
248-
{effectiveWidget === "checkout" && (
240+
{props.widget === "checkout" && (
249241
<div className="space-y-4">
250242
<div className="flex flex-col gap-2">
251243
<Label htmlFor={sellerAddressId}>Seller Address</Label>
@@ -347,7 +339,7 @@ export function LeftSection(props: {
347339
)}
348340

349341
{/* Transaction Mode Options */}
350-
{effectiveWidget === "transaction" && (
342+
{props.widget === "transaction" && (
351343
<div className="space-y-4">
352344
<div className="flex flex-col gap-2">
353345
<Label>Transaction</Label>
@@ -495,26 +487,6 @@ export function LeftSection(props: {
495487
<Label htmlFor={"branding"}>Show Branding</Label>
496488
</div>
497489
</CollapsibleSection>
498-
499-
<CollapsibleSection icon={FuelIcon} title="Sponsor gas fees">
500-
<div className="mt-4 flex items-start gap-6">
501-
<div className="flex flex-col gap-2">
502-
<p className="">
503-
Abstract away gas fees for users of your app by enabling ERC-4337
504-
Account Abstraction
505-
</p>
506-
507-
<Link
508-
className="inline-flex items-center gap-2 text-muted-foreground hover:text-foreground"
509-
href="https://portal.thirdweb.com/wallets/sponsor-gas?utm_source=playground"
510-
target="_blank"
511-
>
512-
Learn more about Account Abstraction
513-
<ExternalLinkIcon className="size-4" />
514-
</Link>
515-
</div>
516-
</div>
517-
</CollapsibleSection>
518490
</div>
519491
);
520492
}

0 commit comments

Comments
 (0)