From 1daba4378427408787616a9c8059567a2472549c Mon Sep 17 00:00:00 2001 From: nicholas Date: Mon, 11 Sep 2023 13:01:47 +0200 Subject: [PATCH] Add 'redirectFromTop' config prop to allow top level redirect when Checkout loaded in an iframe --- .changeset/forty-badgers-bow.md | 5 +++++ .../src/components/Redirect/Redirect.test.tsx | 17 +++++++++++++++++ .../RedirectShopper/RedirectShopper.tsx | 10 +++++++++- packages/lib/src/core/config.ts | 1 + packages/lib/src/utils/detectInIframe.ts | 2 ++ 5 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 .changeset/forty-badgers-bow.md create mode 100644 packages/lib/src/utils/detectInIframe.ts diff --git a/.changeset/forty-badgers-bow.md b/.changeset/forty-badgers-bow.md new file mode 100644 index 000000000..95c319abf --- /dev/null +++ b/.changeset/forty-badgers-bow.md @@ -0,0 +1,5 @@ +--- +'@adyen/adyen-web': minor +--- + +Add 'redirectFromTop' config prop to allow top level redirect when Checkout loaded in an iframe diff --git a/packages/lib/src/components/Redirect/Redirect.test.tsx b/packages/lib/src/components/Redirect/Redirect.test.tsx index d1fd71872..49b46060f 100644 --- a/packages/lib/src/components/Redirect/Redirect.test.tsx +++ b/packages/lib/src/components/Redirect/Redirect.test.tsx @@ -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(); + + expect(wrapper.find('form')).toHaveLength(1); + expect(wrapper.find('form').prop('target')).toBe('_top'); setTimeout(() => expect(window.HTMLFormElement.prototype.submit).toHaveBeenCalled(), 0); }); }); diff --git a/packages/lib/src/components/Redirect/components/RedirectShopper/RedirectShopper.tsx b/packages/lib/src/components/Redirect/components/RedirectShopper/RedirectShopper.tsx index c625c817d..9783fce54 100644 --- a/packages/lib/src/components/Redirect/components/RedirectShopper/RedirectShopper.tsx +++ b/packages/lib/src/components/Redirect/components/RedirectShopper/RedirectShopper.tsx @@ -1,10 +1,12 @@ import { Component, h } from 'preact'; +import detectInIframe from '../../../../utils/detectInIframe'; interface RedirectShopperProps { beforeRedirect: (resolve, reject, url) => Promise; url: string; method: 'GET' | 'POST'; data?: any; + redirectFromTop?: boolean; } class RedirectShopper extends Component { @@ -19,7 +21,12 @@ class RedirectShopper extends Component { if (this.postForm) { this.postForm.submit(); } else { - window.location.assign(this.props.url); + if (this.props.redirectFromTop && 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 { ref={ref => { this.postForm = ref; }} + {...(this.props.redirectFromTop && detectInIframe() && { target: '_top' })} > {Object.keys(data).map(key => ( diff --git a/packages/lib/src/core/config.ts b/packages/lib/src/core/config.ts index 79c438618..3c3aa6da7 100644 --- a/packages/lib/src/core/config.ts +++ b/packages/lib/src/core/config.ts @@ -15,6 +15,7 @@ export const GENERIC_OPTIONS = [ 'session', 'clientKey', 'showPayButton', + 'redirectFromTop', 'installmentOptions', // Events diff --git a/packages/lib/src/utils/detectInIframe.ts b/packages/lib/src/utils/detectInIframe.ts new file mode 100644 index 000000000..5307d7028 --- /dev/null +++ b/packages/lib/src/utils/detectInIframe.ts @@ -0,0 +1,2 @@ +// Returns true if the page is being run in an iframe +export default () => window.location !== window.parent.location;