Skip to content

Commit

Permalink
Merge branch 'main' into dms/refactor-playground-2
Browse files Browse the repository at this point in the history
  • Loading branch information
dschlabach committed Nov 19, 2024
2 parents dd240c6 + 5a41b79 commit 7fe3a1a
Show file tree
Hide file tree
Showing 11 changed files with 483 additions and 3 deletions.
51 changes: 51 additions & 0 deletions playground/nextjs-app-router/components/DemoOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,57 @@ const COMPONENT_CONFIG: Partial<
],
};

const COMMON_OPTIONS = [
ActiveComponent,
ComponentMode,
ComponentTheme,
WalletType,
];

const COMPONENT_CONFIG: Partial<
Record<OnchainKitComponent, (() => React.JSX.Element)[]>
> = {
[OnchainKitComponent.Checkout]: [
Chain,
PaymasterUrl,
IsSponsored,
CheckoutOptions,
],
[OnchainKitComponent.Swap]: [Chain, PaymasterUrl, IsSponsored, SwapConfig],
[OnchainKitComponent.SwapDefault]: [
Chain,
PaymasterUrl,
IsSponsored,
SwapConfig,
],
[OnchainKitComponent.Transaction]: [
Chain,
PaymasterUrl,
IsSponsored,
TransactionOptions,
],
[OnchainKitComponent.TransactionDefault]: [
Chain,
PaymasterUrl,
IsSponsored,
TransactionOptions,
],
[OnchainKitComponent.NFTCard]: [Chain, NFTOptions],
[OnchainKitComponent.NFTCardDefault]: [Chain, NFTOptions],
[OnchainKitComponent.NFTMintCard]: [
Chain,
PaymasterUrl,
IsSponsored,
NFTOptions,
],
[OnchainKitComponent.NFTMintCardDefault]: [
Chain,
PaymasterUrl,
IsSponsored,
NFTOptions,
],
};

export default function DemoOptions({
component,
}: {
Expand Down
1 change: 1 addition & 0 deletions site/docs/components/landing/WalletDemo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const walletDemoCode = `
import {
ConnectWallet,
Wallet,
WalletDefault,
WalletDropdown,
WalletDropdownBasename,
WalletDropdownLink,
Expand Down
2 changes: 1 addition & 1 deletion site/docs/pages/config/onchainkit-provider.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const App = () => {

This prop is required for all OnchainKit components.

We recommend importing chain data from[viem](https://viem.sh/docs/chains/introduction).
We recommend importing chain data from [viem](https://viem.sh/docs/chains/introduction).

---

Expand Down
3 changes: 2 additions & 1 deletion src/fund/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const DEFAULT_ONRAMP_URL = 'https://pay.coinbase.com';
// The base URL for the Coinbase Onramp widget.
export const ONRAMP_BUY_URL = 'https://pay.coinbase.com/buy';
export const ONRAMP_BUY_URL = `${DEFAULT_ONRAMP_URL}/buy`;
// The recommended height of a Coinbase Onramp popup window.
export const ONRAMP_POPUP_HEIGHT = 720;
// The recommended width of a Coinbase Onramp popup window.
Expand Down
2 changes: 2 additions & 0 deletions src/fund/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export { FundButton } from './components/FundButton';
export { getCoinbaseSmartWalletFundUrl } from './utils/getCoinbaseSmartWalletFundUrl';
export { getOnrampBuyUrl } from './utils/getOnrampBuyUrl';
export { setupOnrampEventListeners } from './utils/setupOnrampEventListeners';

export type {
GetOnrampUrlWithProjectIdParams,
GetOnrampUrlWithSessionTokenParams,
Expand Down
69 changes: 69 additions & 0 deletions src/fund/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,72 @@ export type FundButtonReact = {
rel?: string; // Specifies the relationship between the current document and the linked document
target?: string; // Where to open the target if `openIn` is set to tab
};

/**
* Matches a JSON object.
* This type can be useful to enforce some input to be JSON-compatible or as a super-type to be extended from. Don't use this as a direct return type as the user would have to double-cast it: `jsonObject as unknown as CustomResponse`. Instead, you could extend your CustomResponse type from it to ensure your type only uses JSON-compatible types: `interface CustomResponse extends JsonObject { … }`.
* @category JSON
*/
export type JsonObject = { [Key in string]?: JsonValue };

/**
* Matches a JSON array.
* @category JSON
*/
export type JsonArray = JsonValue[];

/**
* Matches any valid JSON primitive value.
* @category JSON
*/
export type JsonPrimitive = string | number | boolean | null;

/**
* Matches any valid JSON value.
* @see `Jsonify` if you need to transform a type to one that is assignable to `JsonValue`.
* @category JSON
*/
export type JsonValue = JsonPrimitive | JsonObject | JsonArray;

export type OpenEvent = {
eventName: 'open';
widgetName: string;
};

export type TransitionViewEvent = {
eventName: 'transition_view';
pageRoute: string;
};

export type PublicErrorEvent = {
eventName: 'error';
error: OnRampError;
};

export type ExitEvent = {
eventName: 'exit';
error?: OnRampError;
};

export type SuccessEvent = {
eventName: 'success';
};

export type RequestOpenUrlEvent = {
eventName: 'request_open_url';
url: string;
};

export type EventMetadata =
| OpenEvent
| TransitionViewEvent
| PublicErrorEvent
| ExitEvent
| SuccessEvent
| RequestOpenUrlEvent;

export type OnRampError = {
errorType: 'internal_error' | 'handled_error' | 'network_error';
code?: string;
debugMessage?: string;
};
110 changes: 110 additions & 0 deletions src/fund/utils/setupOnrampEventListeners.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import type { EventMetadata } from '../types';
import { setupOnrampEventListeners } from './setupOnrampEventListeners';
import { subscribeToWindowMessage } from './subscribeToWindowMessage';

vi.mock('./subscribeToWindowMessage', () => ({
subscribeToWindowMessage: vi.fn(),
}));

describe('setupOnrampEventListeners', () => {
let unsubscribe: ReturnType<typeof vi.fn>;

beforeEach(() => {
unsubscribe = vi.fn();
});

afterEach(() => {
vi.resetAllMocks();
});

it('should call subscribeToWindowMessage with correct parameters', () => {
const onEvent = vi.fn();
const onExit = vi.fn();
const onSuccess = vi.fn();
const host = 'https://example.com';

setupOnrampEventListeners({ onEvent, onExit, onSuccess, host });

expect(subscribeToWindowMessage).toHaveBeenCalledWith({
allowedOrigin: host,
onMessage: expect.any(Function),
});
});

it('should call onSuccess callback when success event is received', () => {
const onEvent = vi.fn();
const onExit = vi.fn();
const onSuccess = vi.fn();
const host = 'https://example.com';

setupOnrampEventListeners({ onEvent, onExit, onSuccess, host });

const eventMetadata: EventMetadata = { eventName: 'success' };

vi.mocked(subscribeToWindowMessage).mock.calls[0][0].onMessage(
eventMetadata,
);

expect(onSuccess).toHaveBeenCalled();
expect(onExit).not.toHaveBeenCalled();
expect(onEvent).toHaveBeenCalledWith(eventMetadata);
});

it('should call onExit callback when exit event is received', () => {
const onEvent = vi.fn();
const onExit = vi.fn();
const onSuccess = vi.fn();
const host = 'https://example.com';

setupOnrampEventListeners({ onEvent, onExit, onSuccess, host });

const eventMetadata: EventMetadata = {
eventName: 'exit',
error: {
errorType: 'internal_error',
},
};
vi.mocked(subscribeToWindowMessage).mock.calls[0][0].onMessage(
eventMetadata,
);

expect(onExit).toHaveBeenCalledWith(eventMetadata.error);
expect(onSuccess).not.toHaveBeenCalled();
expect(onEvent).toHaveBeenCalledWith(eventMetadata);
});

it('should call onEvent callback for any event received', () => {
const onEvent = vi.fn();
const onExit = vi.fn();
const onSuccess = vi.fn();
const host = 'https://example.com';

setupOnrampEventListeners({ onEvent, onExit, onSuccess, host });

const eventMetadata: EventMetadata = { eventName: 'success' };
vi.mocked(subscribeToWindowMessage).mock.calls[0][0].onMessage(
eventMetadata,
);

expect(onEvent).toHaveBeenCalledWith(eventMetadata);
});

it('should return the unsubscribe function', () => {
const onEvent = vi.fn();
const onExit = vi.fn();
const onSuccess = vi.fn();
const host = 'https://example.com';

vi.mocked(subscribeToWindowMessage).mockReturnValue(unsubscribe);

const result = setupOnrampEventListeners({
onEvent,
onExit,
onSuccess,
host,
});

expect(result).toBe(unsubscribe);
});
});
41 changes: 41 additions & 0 deletions src/fund/utils/setupOnrampEventListeners.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { DEFAULT_ONRAMP_URL } from '../constants';
import type { EventMetadata, OnRampError } from '../types';
import { subscribeToWindowMessage } from './subscribeToWindowMessage';

type SetupOnrampEventListenersParams = {
host?: string;
onSuccess?: () => void;
onExit?: (error?: OnRampError) => void;
onEvent?: (event: EventMetadata) => void;
};

/**
* Subscribes to events from the Coinbase Onramp widget.
* @param onEvent - Callback for when any event is received.
* @param onExit - Callback for when an exit event is received.
* @param onSuccess - Callback for when a success event is received.
* @returns a function to unsubscribe from the event listener.
*/
export function setupOnrampEventListeners({
onEvent,
onExit,
onSuccess,
host = DEFAULT_ONRAMP_URL,
}: SetupOnrampEventListenersParams) {
const unsubscribe = subscribeToWindowMessage({
allowedOrigin: host,
onMessage: (data) => {
const metadata = data as EventMetadata;

if (metadata.eventName === 'success') {
onSuccess?.();
}
if (metadata.eventName === 'exit') {
onExit?.(metadata.error);
}
onEvent?.(metadata);
},
});

return unsubscribe;
}
Loading

0 comments on commit 7fe3a1a

Please sign in to comment.