Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove embedded experience #272

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,6 @@ const options = {
},
closeOnExit: true,
closeOnSuccess: true,
embeddedContentStyles: {
target: '#target-area',
},
onExit: () => {
alert('On Exit');
},
Expand Down
2 changes: 1 addition & 1 deletion src/onramp/initOnRamp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export type InitOnRampCallback = {

export const initOnRamp = (
{
experienceLoggedIn = 'embedded', // default experience type
experienceLoggedIn = 'popup', // default experience type
widgetParameters,
...options
}: InitOnRampParams,
Expand Down
11 changes: 1 addition & 10 deletions src/types/widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,10 @@ export type WidgetType = 'buy' | 'checkout';

export type IntegrationType = 'direct' | 'secure_standalone';

export type Experience = 'embedded' | 'popup' | 'new_tab';
export type Experience = 'popup' | 'new_tab';

export type Theme = 'light' | 'dark';

export type EmbeddedContentStyles = {
target?: string;
width?: string;
height?: string;
position?: string;
top?: string;
};

export type CBPayExperienceOptions<T> = {
widgetParameters: T;
target?: string;
Expand All @@ -29,7 +21,6 @@ export type CBPayExperienceOptions<T> = {
onRequestedUrl?: (url: string) => void;
closeOnExit?: boolean;
closeOnSuccess?: boolean;
embeddedContentStyles?: EmbeddedContentStyles;
experienceLoggedIn?: Experience;
experienceLoggedOut?: Experience;
};
2 changes: 0 additions & 2 deletions src/utils/CBPayInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ export class CBPayInstance implements CBPayInstanceType {
widget,
experienceLoggedIn,
experienceLoggedOut,
embeddedContentStyles,
onExit,
onSuccess,
onEvent,
Expand All @@ -65,7 +64,6 @@ export class CBPayInstance implements CBPayInstanceType {
path: widgetRoutes[widget],
experienceLoggedIn,
experienceLoggedOut,
embeddedContentStyles,
onExit: () => {
onExit?.();
if (closeOnExit) {
Expand Down
41 changes: 13 additions & 28 deletions src/utils/CoinbasePixel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import {
PIXEL_ID,
CoinbasePixelConstructorParams,
OpenExperienceOptions,
popupWindowFeatures,
} from './CoinbasePixel';
import { EMBEDDED_IFRAME_ID } from './createEmbeddedContent';

import { broadcastPostMessage, onBroadcastedPostMessage } from './postMessage';

Expand All @@ -25,13 +25,14 @@ describe('CoinbasePixel', () => {
};
const defaultOpenOptions: OpenExperienceOptions = {
path: '/buy',
experienceLoggedIn: 'embedded',
experienceLoggedIn: 'popup',
};

beforeEach(() => {
mockOnReady = jest.fn();
mockOnFallbackOpen = jest.fn();
mockUnsubCallback = jest.fn();
(window.open as jest.Mock).mockReset();
(onBroadcastedPostMessage as jest.Mock).mockReturnValue(mockUnsubCallback);
defaultArgs = {
appId: 'test',
Expand All @@ -43,7 +44,6 @@ describe('CoinbasePixel', () => {

afterEach(() => {
document.getElementById(PIXEL_ID)?.remove();
document.getElementById(EMBEDDED_IFRAME_ID)?.remove();
// @ts-expect-error - test
window.chrome = undefined;
jest.resetAllMocks();
Expand Down Expand Up @@ -154,7 +154,11 @@ describe('CoinbasePixel', () => {

pixel.openExperience(defaultOpenOptions);

expect(document.querySelector(`iframe#${EMBEDDED_IFRAME_ID}`)).toBeTruthy();
expect(window.open).toHaveBeenCalledWith(
'https://pay.coinbase.com/buy?appId=test&type=secure_standalone&nonce=mock-nonce',
'Coinbase',
popupWindowFeatures,
);
});

it('should handle openExperience when pixel has status "loading"', () => {
Expand All @@ -163,7 +167,7 @@ describe('CoinbasePixel', () => {
expect(instance.state).toEqual('loading');
instance.openExperience(defaultOpenOptions);

expect(document.querySelector(`iframe#${EMBEDDED_IFRAME_ID}`)).toBeFalsy();
expect(window.open).not.toHaveBeenCalled();
});

it('should handle openExperience when pixel has status "waiting_for_response"', () => {
Expand All @@ -173,7 +177,7 @@ describe('CoinbasePixel', () => {
instance.openExperience(defaultOpenOptions);

expect(instance.queuedOpenOptions).toBeFalsy();
expect(document.querySelector(`iframe#${EMBEDDED_IFRAME_ID}`)).toBeFalsy();
expect(window.open).not.toHaveBeenCalled();
});

it('should handle openExperience when pixel has status "failed"', () => {
Expand All @@ -183,7 +187,7 @@ describe('CoinbasePixel', () => {
instance.openExperience(defaultOpenOptions);

expect(instance.queuedOpenOptions).toBeFalsy();
expect(document.querySelector(`iframe#${EMBEDDED_IFRAME_ID}`)).toBeFalsy();
expect(window.open).not.toHaveBeenCalled();
});

it('should handle openExperience with no preloaded nonce', () => {
Expand All @@ -195,22 +199,7 @@ describe('CoinbasePixel', () => {
'Attempted to open CB Pay experience without nonce',
);

expect(document.querySelector(`iframe#${EMBEDDED_IFRAME_ID}`)).toBeFalsy();
});

it('should handle opening the embedded experience when logged out', () => {
const instance = createUntypedPixel(defaultArgs);

mockPixelReady(false);
mockOnAppParamsNonce('mock-nonce');
instance.openExperience(defaultOpenOptions);

expect(window.open).toHaveBeenCalledWith(
'https://pay.coinbase.com/signin?appId=test&type=direct',
'Coinbase',
'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, height=730,width=460',
);
expect(findMockedListeners('signin_success')).toHaveLength(1);
expect(window.open).not.toHaveBeenCalled();
});

it('should handle opening the popup experience in chrome extensions', () => {
Expand Down Expand Up @@ -271,7 +260,7 @@ describe('CoinbasePixel', () => {
expect(window.open).toHaveBeenCalledWith(
'https://pay.coinbase.com/buy?appId=test&type=secure_standalone&nonce=mock-nonce',
'Coinbase',
'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, height=730,width=460',
popupWindowFeatures,
);
});

Expand Down Expand Up @@ -358,7 +347,3 @@ function mockOnAppParamsNonce(nonce: string) {
call[1].onMessage({ nonce });
});
}

function findMockedListeners(message: string) {
return (onBroadcastedPostMessage as jest.Mock).mock.calls.filter(([m]) => m === message);
}
52 changes: 22 additions & 30 deletions src/utils/CoinbasePixel.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { DEFAULT_HOST } from '../config';
import { EmbeddedContentStyles, Experience, Theme } from 'types/widget';
import { createEmbeddedContent, EMBEDDED_IFRAME_ID } from './createEmbeddedContent';
import { Experience, Theme } from 'types/widget';
import { JsonObject } from 'types/JsonTypes';
import { broadcastPostMessage, onBroadcastedPostMessage } from './postMessage';
import { EventMetadata } from 'types/events';
Expand Down Expand Up @@ -44,9 +43,21 @@ export type OpenExperienceOptions = {
path: string;
experienceLoggedIn: Experience;
experienceLoggedOut?: Experience;
embeddedContentStyles?: EmbeddedContentStyles;
} & ExperienceListeners;

export const popupWindowFeatures = [
'copyhistory=no',
'directories=no',
'location=no',
'menubar=no',
'resizable=no',
'scrollbars=no',
'status=no',
'toolbar=no',
`height=${PopupSizes.signin.height}`,
`width=${PopupSizes.signin.width}`,
].join(', ');

export class CoinbasePixel {
/**
* Tracks the loading state of the embedded pixel
Expand All @@ -71,6 +82,7 @@ export class CoinbasePixel {
private onReadyCallback: CoinbasePixelConstructorParams['onReady'];
private onFallbackOpen: CoinbasePixelConstructorParams['onFallbackOpen'];
private theme: Theme | null | undefined;
private widgetWindow: Window | null;

public isLoggedIn = false;

Expand All @@ -90,6 +102,7 @@ export class CoinbasePixel {
this.onFallbackOpen = onFallbackOpen;
this.debug = debug || false;
this.theme = theme;
this.widgetWindow = null;

this.addPixelReadyListener();
this.addErrorListener();
Expand Down Expand Up @@ -132,7 +145,7 @@ export class CoinbasePixel {

this.setupExperienceListeners(options);

const { path, experienceLoggedIn, experienceLoggedOut, embeddedContentStyles } = options;
const { path, experienceLoggedIn, experienceLoggedOut } = options;

const widgetUrl = new URL(`${this.host}${path}`);
widgetUrl.searchParams.append('appId', this.appId);
Expand All @@ -151,23 +164,7 @@ export class CoinbasePixel {

this.log('Opening experience', { experience, isLoggedIn: this.isLoggedIn });

if (experience === 'embedded') {
const openEmbeddedExperience = () => {
const embedded = createEmbeddedContent({ url, ...embeddedContentStyles });
if (embeddedContentStyles?.target) {
document.querySelector(embeddedContentStyles?.target)?.replaceChildren(embedded);
} else {
document.body.appendChild(embedded);
}
};

if (!this.isLoggedIn) {
// Embedded experience opens popup for signin
this.startDirectSignin(openEmbeddedExperience);
} else {
openEmbeddedExperience();
}
} else if (experience === 'popup' && window.chrome?.windows?.create) {
if (experience === 'popup' && window.chrome?.windows?.create) {
void window.chrome.windows.create(
{
url,
Expand Down Expand Up @@ -195,7 +192,7 @@ export class CoinbasePixel {
} else if (experience === 'new_tab' && window.chrome?.tabs?.create) {
void window.chrome.tabs.create({ url });
} else {
openWindow(url, experience);
this.widgetWindow = openWindow(url, experience);
}

// For users who exit the experience and want to re-enter, we need a fresh nonce to use.
Expand All @@ -209,7 +206,8 @@ export class CoinbasePixel {
};

public endExperience = (): void => {
document.getElementById(EMBEDDED_IFRAME_ID)?.remove();
this.widgetWindow?.close();
this.widgetWindow = null;
};

public destroy = (): void => {
Expand Down Expand Up @@ -394,11 +392,5 @@ function createPixel({ host, appId }: { host: string; appId: string }) {
}

function openWindow(url: string, experience: Experience) {
return window.open(
url,
'Coinbase',
experience === 'popup'
? `toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, height=${PopupSizes.signin.height},width=${PopupSizes.signin.width}`
: undefined,
);
return window.open(url, 'Coinbase', experience === 'popup' ? popupWindowFeatures : undefined);
}
46 changes: 0 additions & 46 deletions src/utils/createEmbeddedContent.test.ts

This file was deleted.

27 changes: 0 additions & 27 deletions src/utils/createEmbeddedContent.ts

This file was deleted.

Loading