Add 'redirectFromTop' config prop to allow top level redirect when Checkout loaded in an iframe (#2325)

* Add 'redirectFromTop' config prop to allow top level redirect when Checkout loaded in an iframe

* Changed 'redirectFromTop' to 'redirectFromTopWhenInIframe' to make its function clearer
This commit is contained in:
sponglord
2023-09-11 15:49:23 +02:00
committed by GitHub
parent d7740c4839
commit 0f5f834301
5 changed files with 34 additions and 1 deletions

View File

@@ -0,0 +1,5 @@
---
'@adyen/adyen-web': minor
---
Add 'redirectFromTopWhenInIframe' config prop to allow top level redirect when Checkout loaded in an iframe

View File

@@ -3,6 +3,12 @@ import { h } from 'preact';
import Redirect from './Redirect';
import RedirectShopper from './components/RedirectShopper';
jest.mock('../../utils/detectInIframe', () => {
return jest.fn().mockImplementation(() => {
return true;
});
});
describe('Redirect', () => {
describe('isValid', () => {
test('Is always valid', () => {
@@ -19,6 +25,17 @@ describe('Redirect', () => {
expect(wrapper.find('form')).toHaveLength(1);
expect(wrapper.find('form').prop('action')).toBe('http://www.adyen.com');
expect(wrapper.find('form').prop('target')).toBe(undefined);
setTimeout(() => expect(window.HTMLFormElement.prototype.submit).toHaveBeenCalled(), 0);
});
test('Accepts a POST redirect status, setting target to _top, when the config prop tells it to', () => {
window.HTMLFormElement.prototype.submit = jest.fn();
const wrapper = mount(<RedirectShopper url="http://www.adyen.com" method="POST" data={{}} redirectFromTopWhenInIframe={true} />);
expect(wrapper.find('form')).toHaveLength(1);
expect(wrapper.find('form').prop('target')).toBe('_top');
setTimeout(() => expect(window.HTMLFormElement.prototype.submit).toHaveBeenCalled(), 0);
});
});

View File

@@ -1,10 +1,12 @@
import { Component, h } from 'preact';
import detectInIframe from '../../../../utils/detectInIframe';
interface RedirectShopperProps {
beforeRedirect: (resolve, reject, url) => Promise<void>;
url: string;
method: 'GET' | 'POST';
data?: any;
redirectFromTopWhenInIframe?: boolean;
}
class RedirectShopper extends Component<RedirectShopperProps> {
@@ -19,7 +21,12 @@ class RedirectShopper extends Component<RedirectShopperProps> {
if (this.postForm) {
this.postForm.submit();
} else {
window.location.assign(this.props.url);
if (this.props.redirectFromTopWhenInIframe && detectInIframe()) {
// if in an iframe and the config prop allows it - try to redirect from the top level window
window.top.location.assign?.(this.props.url);
} else {
window.location.assign(this.props.url);
}
}
};
@@ -44,6 +51,7 @@ class RedirectShopper extends Component<RedirectShopperProps> {
ref={ref => {
this.postForm = ref;
}}
{...(this.props.redirectFromTopWhenInIframe && detectInIframe() && { target: '_top' })}
>
{Object.keys(data).map(key => (
<input type="hidden" name={key} key={key} value={data[key]} />

View File

@@ -15,6 +15,7 @@ export const GENERIC_OPTIONS = [
'session',
'clientKey',
'showPayButton',
'redirectFromTopWhenInIframe',
'installmentOptions',
// Events

View File

@@ -0,0 +1,2 @@
// Returns true if the page is being run in an iframe
export default () => window.location !== window.parent.location;