From d35cad865526c69c3bff400e81d1c30cd48b0a9a Mon Sep 17 00:00:00 2001 From: MananTank Date: Wed, 7 Jan 2026 20:23:15 +0000 Subject: [PATCH] [MNY-355] Playground: Add Buy Widget iframe playground (#8615) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ## PR-Codex overview This PR focuses on enhancing the `buy` widget functionality by introducing iframe support and improving the handling of integration types. It also adds new components for rendering iframe previews and updates the URL management for better integration experience. ### Detailed summary - Updated `LeftSection.tsx` to allow both `checkout` and `buy` widgets for iframe integration. - Modified `Page` component in `page.tsx` to handle async `searchParams` and pass `defaultTab` to `BuyPlayground`. - Enhanced `CodeGen` to support widget type in iframe code generation. - Added `CheckoutIframePreview` and `BuyWidgetIframePreview` components in `RightSection.tsx` for rendering respective iframes. - Created `buildBuyIframeUrl` function in `buildBuyIframeUrl.ts` for generating URLs for the buy widget. - Updated `BuyPlayground` to manage state and URL for integration type selection using tabs. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` --- .../app/bridge/buy-widget/BuyPlayground.tsx | 73 +++++++++++++++++-- .../src/app/bridge/buy-widget/page.tsx | 12 ++- .../src/app/bridge/components/CodeGen.tsx | 21 ++++-- .../src/app/bridge/components/LeftSection.tsx | 3 +- .../app/bridge/components/RightSection.tsx | 64 ++++++++++++---- .../bridge/components/buildBuyIframeUrl.ts | 72 ++++++++++++++++++ 6 files changed, 214 insertions(+), 31 deletions(-) create mode 100644 apps/playground-web/src/app/bridge/components/buildBuyIframeUrl.ts diff --git a/apps/playground-web/src/app/bridge/buy-widget/BuyPlayground.tsx b/apps/playground-web/src/app/bridge/buy-widget/BuyPlayground.tsx index 93544d17ab0..a068b4d2959 100644 --- a/apps/playground-web/src/app/bridge/buy-widget/BuyPlayground.tsx +++ b/apps/playground-web/src/app/bridge/buy-widget/BuyPlayground.tsx @@ -1,12 +1,15 @@ "use client"; -import { useState } from "react"; +import { useTheme } from "next-themes"; +import { useEffect, useState } from "react"; import { arbitrum } from "thirdweb/chains"; +import { TabButtons } from "@/components/ui/tab-buttons"; import { LeftSection } from "../components/LeftSection"; import { RightSection } from "../components/RightSection"; import type { BridgeComponentsPlaygroundOptions } from "../components/types"; const defaultOptions: BridgeComponentsPlaygroundOptions = { + integrationType: "react", payOptions: { buyTokenAddress: undefined, buyTokenAmount: "0.002", @@ -29,16 +32,70 @@ const defaultOptions: BridgeComponentsPlaygroundOptions = { }, }; -export function BuyPlayground() { - const [options, setOptions] = - useState(defaultOptions); +function updatePageUrl( + tab: BridgeComponentsPlaygroundOptions["integrationType"], +) { + const url = new URL(window.location.href); + if (tab === defaultOptions.integrationType) { + url.searchParams.delete("tab"); + } else { + url.searchParams.set("tab", tab || ""); + } + + window.history.replaceState({}, "", url.toString()); +} + +export function BuyPlayground(props: { defaultTab?: "iframe" | "react" }) { + const { theme } = useTheme(); + + const [options, setOptions] = useState( + () => ({ + ...defaultOptions, + integrationType: props.defaultTab || defaultOptions.integrationType, + }), + ); + + // Change theme on global theme change + useEffect(() => { + setOptions((prev) => ({ + ...prev, + theme: { + ...prev.theme, + type: theme === "dark" ? "dark" : "light", + }, + })); + }, [theme]); + + useEffect(() => { + updatePageUrl(options.integrationType); + }, [options.integrationType]); return ( -
-
- +
+ setOptions({ ...options, integrationType: "react" }), + isActive: options.integrationType === "react", + }, + { + name: "Iframe", + onClick: () => + setOptions({ ...options, integrationType: "iframe" }), + isActive: options.integrationType === "iframe", + }, + ]} + /> + +
+ +
+
+ +
+
-
); } diff --git a/apps/playground-web/src/app/bridge/buy-widget/page.tsx b/apps/playground-web/src/app/bridge/buy-widget/page.tsx index 9101e859d4c..5b5ccec7757 100644 --- a/apps/playground-web/src/app/bridge/buy-widget/page.tsx +++ b/apps/playground-web/src/app/bridge/buy-widget/page.tsx @@ -19,7 +19,15 @@ export const metadata = createMetadata({ }, }); -export default function Page() { +export default async function Page(props: { + searchParams: Promise<{ tab?: string }>; +}) { + const searchParams = await props.searchParams; + const defaultTab = + searchParams.tab === "iframe" || searchParams.tab === "react" + ? searchParams.tab + : undefined; + return ( - + ); diff --git a/apps/playground-web/src/app/bridge/components/CodeGen.tsx b/apps/playground-web/src/app/bridge/components/CodeGen.tsx index 0578f2fa115..d364dc4599e 100644 --- a/apps/playground-web/src/app/bridge/components/CodeGen.tsx +++ b/apps/playground-web/src/app/bridge/components/CodeGen.tsx @@ -5,6 +5,7 @@ import { stringifyImports, stringifyProps, } from "../../../lib/code-gen"; +import { buildBuyIframeUrl } from "./buildBuyIframeUrl"; import { buildCheckoutIframeUrl } from "./buildCheckoutIframeUrl"; import type { BridgeComponentsPlaygroundOptions } from "./types"; @@ -27,7 +28,8 @@ export function CodeGen(props: { options: BridgeComponentsPlaygroundOptions; }) { const isIframe = - props.widget === "checkout" && props.options.integrationType === "iframe"; + (props.widget === "checkout" || props.widget === "buy") && + props.options.integrationType === "iframe"; return (
@@ -36,7 +38,7 @@ export function CodeGen(props: { className="grow" code={ isIframe - ? getIframeCode(props.options) + ? getIframeCode(props.widget as "buy" | "checkout", props.options) : getCode(props.widget, props.options) } lang={isIframe ? "html" : "tsx"} @@ -46,13 +48,22 @@ export function CodeGen(props: { ); } -function getIframeCode(options: BridgeComponentsPlaygroundOptions) { - const src = buildCheckoutIframeUrl(options); +function getIframeCode( + widget: "buy" | "checkout", + options: BridgeComponentsPlaygroundOptions, +) { + const src = + widget === "buy" + ? buildBuyIframeUrl(options) + : buildCheckoutIframeUrl(options); + + const hasImage = src.includes("image="); + const height = hasImage ? "850px" : "700px"; return `\