@wf-financing/ui 1.4.5 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/index-DgBzPfcG.mjs +76600 -0
- package/dist/index.es.js +1 -75797
- package/dist/profiler-Dvl3kCjE.mjs +240 -0
- package/dist/startRecording-C7EyZWpe.mjs +1225 -0
- package/package.json +2 -2
- package/sdk/index.ts +12 -19
- package/src/App.tsx +9 -8
- package/src/api/continueHostedApplication.ts +3 -3
- package/src/api/ctaBanner.ts +3 -3
- package/src/api/dismissCta.ts +4 -6
- package/src/api/fetchCtaBanner.test.ts +5 -6
- package/src/api/getHeadlessSdkInstance.test.ts +6 -7
- package/src/api/getHeadlessSdkInstance.ts +6 -9
- package/src/api/startHostedApplication.test.ts +5 -6
- package/src/api/startHostedApplication.ts +3 -3
- package/src/components/banner/CtaBanner.snapshot.stories.tsx +7 -40
- package/src/components/modal/ConsentModal.snapshot.stories.tsx +1 -3
- package/src/config/index.ts +2 -0
- package/src/config/url.ts +1 -1
- package/src/config/whitelistedPartnerIds.ts +14 -0
- package/src/hooks/useContinueHostedApplication.ts +2 -2
- package/src/hooks/useCtaBanner.ts +3 -3
- package/src/hooks/useDismissCta.ts +2 -2
- package/src/hooks/useStartHostedApplication.ts +2 -2
- package/src/main.tsx +8 -8
- package/src/utils/applyFont.ts +4 -7
- package/src/utils/getPartnerIdFromToken.ts +9 -0
- package/src/utils/getPartnerThemeById.ts +9 -0
- package/src/utils/index.ts +4 -2
- package/src/utils/initializeHeadlessSdk.ts +4 -8
- package/src/utils/loadScriptAndInitializeSdk.ts +3 -3
- package/src/utils/parseJwt.ts +12 -0
- package/src/utils/partnerContext.ts +2 -3
- package/vitest.shims.d.ts +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wf-financing/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"exports": {
|
|
5
5
|
".": {
|
|
6
6
|
"import": "./dist/index.es.js",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"react-aria": "^3.41.1",
|
|
38
38
|
"react-intl": "^6.2.5",
|
|
39
39
|
"styled-components": "^6.1.19",
|
|
40
|
-
"@wf-financing/embedded-types": "0.
|
|
40
|
+
"@wf-financing/embedded-types": "0.4.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"@wayflyer/flyui": "204.4.1"
|
package/sdk/index.ts
CHANGED
|
@@ -1,36 +1,29 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { PartnerCallbackType, MockedModeType } from '@wf-financing/embedded-types';
|
|
1
|
+
import { PartnerCallbackType, SdkOptionsType } from '@wf-financing/embedded-types';
|
|
3
2
|
|
|
3
|
+
import { PartnerTheme } from '../src/config';
|
|
4
4
|
import { mountToTarget } from '../src/main';
|
|
5
|
+
import { getPartnerIdFromToken, getPartnerThemeById } from '../src/utils';
|
|
5
6
|
|
|
6
7
|
export class WayflyerUiCtaSdk {
|
|
7
|
-
private
|
|
8
|
-
private partnerDesignId: Themes;
|
|
9
|
-
private partnerCallback: PartnerCallbackType;
|
|
10
|
-
private companyToken: string;
|
|
11
|
-
private mockedMode?: MockedModeType;
|
|
8
|
+
private readonly partnerTheme: PartnerTheme;
|
|
12
9
|
|
|
13
10
|
constructor(
|
|
14
|
-
targetId: string,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
mockedMode?: MockedModeType,
|
|
11
|
+
private readonly targetId: string,
|
|
12
|
+
private readonly partnerCallback: PartnerCallbackType,
|
|
13
|
+
private readonly companyToken: string,
|
|
14
|
+
private readonly options?: SdkOptionsType,
|
|
19
15
|
) {
|
|
20
|
-
|
|
21
|
-
this.
|
|
22
|
-
this.partnerCallback = partnerCallback;
|
|
23
|
-
this.companyToken = companyToken;
|
|
24
|
-
this.mockedMode = mockedMode;
|
|
16
|
+
const partnerId = getPartnerIdFromToken(companyToken);
|
|
17
|
+
this.partnerTheme = getPartnerThemeById(partnerId);
|
|
25
18
|
}
|
|
26
19
|
|
|
27
20
|
mountCta() {
|
|
28
21
|
if (document.readyState === 'loading') {
|
|
29
22
|
document.addEventListener('DOMContentLoaded', () =>
|
|
30
|
-
mountToTarget(this.targetId, this.
|
|
23
|
+
mountToTarget(this.targetId, this.partnerTheme, this.partnerCallback, this.companyToken, this.options),
|
|
31
24
|
);
|
|
32
25
|
} else {
|
|
33
|
-
mountToTarget(this.targetId, this.
|
|
26
|
+
mountToTarget(this.targetId, this.partnerTheme, this.partnerCallback, this.companyToken, this.options);
|
|
34
27
|
}
|
|
35
28
|
}
|
|
36
29
|
}
|
package/src/App.tsx
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
2
|
-
import { FlyUIProvider
|
|
3
|
-
import { PartnerCallbackType,
|
|
2
|
+
import { FlyUIProvider } from '@wayflyer/flyui';
|
|
3
|
+
import { PartnerCallbackType, SdkOptionsType } from '@wf-financing/embedded-types';
|
|
4
4
|
import { createIntl, createIntlCache, IntlShape } from 'react-intl';
|
|
5
5
|
|
|
6
|
+
import { PartnerTheme } from './config';
|
|
6
7
|
import { CtaWidget } from './CtaWidget';
|
|
7
8
|
import enMessages from './locales/en.json';
|
|
8
9
|
import { PartnerContext } from './utils';
|
|
9
10
|
|
|
10
11
|
type AppPropsType = {
|
|
11
|
-
|
|
12
|
+
theme: PartnerTheme;
|
|
12
13
|
companyToken: string;
|
|
13
|
-
|
|
14
|
+
options?: SdkOptionsType;
|
|
14
15
|
partnerCallback: PartnerCallbackType;
|
|
15
16
|
onWidgetClose: () => void;
|
|
16
17
|
portalContainer: HTMLElement;
|
|
@@ -22,9 +23,9 @@ const messages = {
|
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
export const App = ({
|
|
25
|
-
|
|
26
|
+
theme,
|
|
26
27
|
companyToken,
|
|
27
|
-
|
|
28
|
+
options,
|
|
28
29
|
partnerCallback,
|
|
29
30
|
onWidgetClose,
|
|
30
31
|
portalContainer,
|
|
@@ -41,8 +42,8 @@ export const App = ({
|
|
|
41
42
|
|
|
42
43
|
return (
|
|
43
44
|
<QueryClientProvider client={queryClient}>
|
|
44
|
-
<PartnerContext.Provider value={{ companyToken,
|
|
45
|
-
<FlyUIProvider theme={
|
|
45
|
+
<PartnerContext.Provider value={{ companyToken, options, partnerCallback, onWidgetClose, portalContainer }}>
|
|
46
|
+
<FlyUIProvider theme={theme} intl={intl}>
|
|
46
47
|
<CtaWidget />
|
|
47
48
|
</FlyUIProvider>
|
|
48
49
|
</PartnerContext.Provider>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { ContinueHostedApplicationResponseType,
|
|
1
|
+
import { ContinueHostedApplicationResponseType, SdkOptionsType } from '@wf-financing/embedded-types';
|
|
2
2
|
|
|
3
3
|
import { getHeadlessSdkInstance } from './getHeadlessSdkInstance';
|
|
4
4
|
|
|
5
5
|
export const continueHostedApplication = async (
|
|
6
6
|
companyToken: string,
|
|
7
|
-
|
|
7
|
+
options?: SdkOptionsType,
|
|
8
8
|
): Promise<ContinueHostedApplicationResponseType> => {
|
|
9
|
-
const sdk = await getHeadlessSdkInstance(companyToken,
|
|
9
|
+
const sdk = await getHeadlessSdkInstance(companyToken, options);
|
|
10
10
|
|
|
11
11
|
return await sdk.continueHostedApplication();
|
|
12
12
|
};
|
package/src/api/ctaBanner.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SdkOptionsType } from '@wf-financing/embedded-types';
|
|
2
2
|
|
|
3
3
|
import { getHeadlessSdkInstance } from './getHeadlessSdkInstance';
|
|
4
4
|
|
|
5
|
-
export const fetchCtaBanner = async (companyToken: string,
|
|
6
|
-
const sdk = await getHeadlessSdkInstance(companyToken,
|
|
5
|
+
export const fetchCtaBanner = async (companyToken: string, options?: SdkOptionsType) => {
|
|
6
|
+
const sdk = await getHeadlessSdkInstance(companyToken, options);
|
|
7
7
|
const cta = await sdk.getCta();
|
|
8
8
|
|
|
9
9
|
return cta;
|
package/src/api/dismissCta.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SdkOptionsType } from '@wf-financing/embedded-types';
|
|
2
2
|
|
|
3
3
|
import { getHeadlessSdkInstance } from './getHeadlessSdkInstance';
|
|
4
4
|
|
|
5
|
-
export const dismissCta = async (companyToken: string,
|
|
6
|
-
const sdk = await getHeadlessSdkInstance(companyToken,
|
|
5
|
+
export const dismissCta = async (companyToken: string, options?: SdkOptionsType) => {
|
|
6
|
+
const sdk = await getHeadlessSdkInstance(companyToken, options);
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
await sdk.dismissCta();
|
|
10
|
-
}
|
|
8
|
+
await sdk.dismissCta();
|
|
11
9
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SdkOptionsType } from '@wf-financing/embedded-types';
|
|
2
2
|
import { afterEach, describe, expect, test, vi } from 'vitest';
|
|
3
3
|
|
|
4
4
|
import { fetchCtaBanner } from './ctaBanner';
|
|
@@ -22,14 +22,13 @@ describe('fetchCtaBanner', () => {
|
|
|
22
22
|
const mockCta = { banner: 'hello' };
|
|
23
23
|
(getHeadlessSdkInstance as any).mockResolvedValueOnce(fakeSdk);
|
|
24
24
|
fakeSdk.getCta.mockResolvedValueOnce(mockCta);
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
sdkScenario: SdkScenarios.NEW_APPLICATION,
|
|
25
|
+
const options: SdkOptionsType = {
|
|
26
|
+
isSandbox: true,
|
|
28
27
|
};
|
|
29
28
|
|
|
30
|
-
const result = await fetchCtaBanner('TOKEN',
|
|
29
|
+
const result = await fetchCtaBanner('TOKEN', options);
|
|
31
30
|
|
|
32
|
-
expect(getHeadlessSdkInstance).toHaveBeenCalledWith('TOKEN',
|
|
31
|
+
expect(getHeadlessSdkInstance).toHaveBeenCalledWith('TOKEN', options);
|
|
33
32
|
expect(fakeSdk.getCta).toHaveBeenCalled();
|
|
34
33
|
expect(result).toBe(mockCta);
|
|
35
34
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SdkOptionsType } from '@wf-financing/embedded-types';
|
|
2
2
|
import { afterEach, describe, expect, Mock, test, vi } from 'vitest';
|
|
3
3
|
|
|
4
4
|
vi.mock('./getHeadlessSdkInstance', () => ({
|
|
@@ -13,9 +13,8 @@ const fakeSdk = {
|
|
|
13
13
|
startHostedApplication: vi.fn(),
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
sdkScenario: SdkScenarios.NEW_APPLICATION,
|
|
16
|
+
const options: SdkOptionsType = {
|
|
17
|
+
isSandbox: true,
|
|
19
18
|
};
|
|
20
19
|
|
|
21
20
|
describe('startHostedApplication', () => {
|
|
@@ -31,9 +30,9 @@ describe('startHostedApplication', () => {
|
|
|
31
30
|
(getHeadlessSdkInstance as unknown as Mock).mockResolvedValueOnce(fakeSdk);
|
|
32
31
|
fakeSdk.startHostedApplication.mockResolvedValueOnce(fakeNextUrl);
|
|
33
32
|
|
|
34
|
-
const result = await startHostedApplication('TOKEN', partnerCallback,
|
|
33
|
+
const result = await startHostedApplication('TOKEN', partnerCallback, options);
|
|
35
34
|
|
|
36
|
-
expect(getHeadlessSdkInstance).toHaveBeenCalledWith('TOKEN',
|
|
35
|
+
expect(getHeadlessSdkInstance).toHaveBeenCalledWith('TOKEN', options);
|
|
37
36
|
expect(partnerCallback).toHaveBeenCalled();
|
|
38
37
|
expect(fakeSdk.startHostedApplication).toHaveBeenCalledWith(fakePartnerData);
|
|
39
38
|
expect(result).toBe(fakeNextUrl);
|
|
@@ -46,7 +45,7 @@ describe('startHostedApplication', () => {
|
|
|
46
45
|
(getHeadlessSdkInstance as unknown as Mock).mockResolvedValueOnce(fakeSdk);
|
|
47
46
|
fakeSdk.startHostedApplication.mockResolvedValueOnce(fakeNextUrl);
|
|
48
47
|
|
|
49
|
-
const result = await startHostedApplication('TOKEN', partnerCallback
|
|
48
|
+
const result = await startHostedApplication('TOKEN', partnerCallback);
|
|
50
49
|
|
|
51
50
|
expect(fakeSdk.startHostedApplication).toHaveBeenCalledWith({});
|
|
52
51
|
expect(result).toBe(fakeNextUrl);
|
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
import { IHeadlessWayflyerSdk,
|
|
1
|
+
import { IHeadlessWayflyerSdk, SdkOptionsType } from '@wf-financing/embedded-types';
|
|
2
2
|
|
|
3
3
|
import { HEADLESS_SDK_URL, WAYFLYER_HEADLESS_SDK_ID } from '../config';
|
|
4
4
|
import { initializeHeadlessSdk, loadScriptAndInitializeSdk } from '../utils';
|
|
5
5
|
|
|
6
6
|
let cachedSdkInstance: IHeadlessWayflyerSdk | null = null;
|
|
7
7
|
|
|
8
|
-
export const getHeadlessSdkInstance = async (companyToken: string,
|
|
8
|
+
export const getHeadlessSdkInstance = async (companyToken: string, options?: SdkOptionsType) => {
|
|
9
9
|
try {
|
|
10
10
|
if (cachedSdkInstance) {
|
|
11
|
-
|
|
12
|
-
cachedSdkInstance.setSdkScenario(mockedMode.sdkScenario);
|
|
13
|
-
}
|
|
11
|
+
cachedSdkInstance.setCompanyToken(companyToken);
|
|
14
12
|
|
|
15
13
|
return cachedSdkInstance;
|
|
16
14
|
}
|
|
@@ -18,11 +16,11 @@ export const getHeadlessSdkInstance = async (companyToken: string, mockedMode?:
|
|
|
18
16
|
const existingScript = document.getElementById(WAYFLYER_HEADLESS_SDK_ID) as HTMLScriptElement;
|
|
19
17
|
|
|
20
18
|
if (window.WayflyerHeadlessSdk) {
|
|
21
|
-
return initializeHeadlessSdk(companyToken,
|
|
19
|
+
return initializeHeadlessSdk(companyToken, options);
|
|
22
20
|
}
|
|
23
21
|
|
|
24
22
|
if (existingScript) {
|
|
25
|
-
return loadScriptAndInitializeSdk(existingScript, companyToken,
|
|
23
|
+
return loadScriptAndInitializeSdk(existingScript, companyToken, options);
|
|
26
24
|
}
|
|
27
25
|
|
|
28
26
|
const script = document.createElement('script');
|
|
@@ -32,8 +30,7 @@ export const getHeadlessSdkInstance = async (companyToken: string, mockedMode?:
|
|
|
32
30
|
script.async = true;
|
|
33
31
|
|
|
34
32
|
document.head.appendChild(script);
|
|
35
|
-
|
|
36
|
-
const headlessSdk: IHeadlessWayflyerSdk = await loadScriptAndInitializeSdk(script, companyToken, mockedMode);
|
|
33
|
+
const headlessSdk: IHeadlessWayflyerSdk = await loadScriptAndInitializeSdk(script, companyToken, options);
|
|
37
34
|
cachedSdkInstance = headlessSdk;
|
|
38
35
|
|
|
39
36
|
return headlessSdk;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SdkOptionsType } from '@wf-financing/embedded-types';
|
|
2
2
|
import { afterEach, describe, expect, test, vi } from 'vitest';
|
|
3
3
|
|
|
4
4
|
vi.mock('./getHeadlessSdkInstance', () => ({
|
|
@@ -25,14 +25,13 @@ describe('startHostedApplication', () => {
|
|
|
25
25
|
|
|
26
26
|
(getHeadlessSdkInstance as unknown as ReturnType<typeof vi.fn>).mockResolvedValueOnce(fakeSdk);
|
|
27
27
|
fakeSdk.startHostedApplication.mockResolvedValueOnce(fakeNextUrl);
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
sdkScenario: SdkScenarios.NEW_APPLICATION,
|
|
28
|
+
const options: SdkOptionsType = {
|
|
29
|
+
isSandbox: true,
|
|
31
30
|
};
|
|
32
31
|
|
|
33
|
-
const result = await startHostedApplication('TOKEN', partnerCallback,
|
|
32
|
+
const result = await startHostedApplication('TOKEN', partnerCallback, options);
|
|
34
33
|
|
|
35
|
-
expect(getHeadlessSdkInstance).toHaveBeenCalledWith('TOKEN',
|
|
34
|
+
expect(getHeadlessSdkInstance).toHaveBeenCalledWith('TOKEN', options);
|
|
36
35
|
expect(partnerCallback).toHaveBeenCalled();
|
|
37
36
|
expect(fakeSdk.startHostedApplication).toHaveBeenCalledWith(fakePartnerData);
|
|
38
37
|
expect(result).toBe(fakeNextUrl);
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { PartnerCallbackType,
|
|
1
|
+
import { PartnerCallbackType, SdkOptionsType } from '@wf-financing/embedded-types';
|
|
2
2
|
|
|
3
3
|
import { getHeadlessSdkInstance } from './getHeadlessSdkInstance';
|
|
4
4
|
|
|
5
5
|
export const startHostedApplication = async (
|
|
6
6
|
companyToken: string,
|
|
7
7
|
partnerCallback: PartnerCallbackType,
|
|
8
|
-
|
|
8
|
+
options?: SdkOptionsType,
|
|
9
9
|
) => {
|
|
10
10
|
let partnerData = await partnerCallback();
|
|
11
|
-
const sdk = await getHeadlessSdkInstance(companyToken,
|
|
11
|
+
const sdk = await getHeadlessSdkInstance(companyToken, options);
|
|
12
12
|
|
|
13
13
|
if (!partnerData) {
|
|
14
14
|
partnerData = {};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Meta, StoryObj } from '@storybook/react';
|
|
2
|
-
import {
|
|
3
|
-
import { SdkScenarios, MockedModeType } from '@wf-financing/embedded-types';
|
|
2
|
+
import { PartnerTheme } from '../../config';
|
|
4
3
|
import { App } from '../../App';
|
|
5
4
|
import { CtaBanner } from './CtaBanner';
|
|
6
5
|
|
|
@@ -8,14 +7,17 @@ const portalContainer = document.createElement('div');
|
|
|
8
7
|
document.body.append(portalContainer);
|
|
9
8
|
|
|
10
9
|
const defaultArgs = {
|
|
11
|
-
|
|
10
|
+
theme: 'whiteLabel' as PartnerTheme,
|
|
12
11
|
companyToken: 'demo-token',
|
|
13
12
|
partnerCallback: () => {},
|
|
14
13
|
onWidgetClose: () => {},
|
|
15
14
|
portalContainer,
|
|
15
|
+
options: {
|
|
16
|
+
isMockedMode: true,
|
|
17
|
+
},
|
|
16
18
|
};
|
|
17
19
|
|
|
18
|
-
type CtaBannerStoryArgs = typeof defaultArgs
|
|
20
|
+
type CtaBannerStoryArgs = typeof defaultArgs;
|
|
19
21
|
|
|
20
22
|
const Template = (args: CtaBannerStoryArgs) => (
|
|
21
23
|
<App {...args}>
|
|
@@ -26,17 +28,8 @@ const Template = (args: CtaBannerStoryArgs) => (
|
|
|
26
28
|
const meta: Meta<typeof CtaBanner> = {
|
|
27
29
|
title: 'CtaBanner',
|
|
28
30
|
component: CtaBanner,
|
|
29
|
-
argTypes: {
|
|
30
|
-
mockedMode: {
|
|
31
|
-
control: { type: 'object' },
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
31
|
args: {
|
|
35
32
|
...defaultArgs,
|
|
36
|
-
mockedMode: {
|
|
37
|
-
isMockedMode: true,
|
|
38
|
-
sdkScenario: SdkScenarios.GENERIC_NEW_APPLICATION,
|
|
39
|
-
},
|
|
40
33
|
},
|
|
41
34
|
decorators: [(_, context) => <Template {...(context.args as CtaBannerStoryArgs)} />],
|
|
42
35
|
};
|
|
@@ -45,31 +38,5 @@ export default meta;
|
|
|
45
38
|
type Story = StoryObj<typeof CtaBanner>;
|
|
46
39
|
|
|
47
40
|
export const GenericOffer: Story = {
|
|
48
|
-
name: '
|
|
49
|
-
args: {
|
|
50
|
-
mockedMode: {
|
|
51
|
-
isMockedMode: true,
|
|
52
|
-
sdkScenario: SdkScenarios.GENERIC_NEW_APPLICATION,
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
export const IndicativeOffer: Story = {
|
|
58
|
-
name: 'Indicative Offer',
|
|
59
|
-
args: {
|
|
60
|
-
mockedMode: {
|
|
61
|
-
isMockedMode: true,
|
|
62
|
-
sdkScenario: SdkScenarios.INDICATIVE_NEW_APPLICATION,
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export const ContinueApplication: Story = {
|
|
68
|
-
name: 'Continue Application',
|
|
69
|
-
args: {
|
|
70
|
-
mockedMode: {
|
|
71
|
-
isMockedMode: true,
|
|
72
|
-
sdkScenario: SdkScenarios.CONTINUE_APPLICATION,
|
|
73
|
-
},
|
|
74
|
-
},
|
|
41
|
+
name: 'CTA',
|
|
75
42
|
};
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
2
|
|
|
3
|
-
import { ConsentModal } from './ConsentModal';
|
|
4
3
|
import { PartnerContext } from '../../utils';
|
|
5
|
-
import {
|
|
4
|
+
import { ConsentModal } from './ConsentModal';
|
|
6
5
|
|
|
7
6
|
const portalContainer = document.createElement('div');
|
|
8
7
|
document.body.append(portalContainer);
|
|
9
8
|
|
|
10
9
|
const appArgs = {
|
|
11
|
-
partnerDesignId: 'whiteLabel' as Themes,
|
|
12
10
|
companyToken: 'demo-token',
|
|
13
11
|
partnerCallback: () => {},
|
|
14
12
|
onWidgetClose: () => {},
|
package/src/config/index.ts
CHANGED
|
@@ -3,3 +3,5 @@ export type { FontParamsType } from './fontParameters';
|
|
|
3
3
|
export { ROOTS_CONFIG } from './rootsParameters';
|
|
4
4
|
export { WAYFLYER_HEADLESS_SDK_ID } from './scriptId';
|
|
5
5
|
export { HEADLESS_SDK_URL } from './url';
|
|
6
|
+
export { WHITELISTED_PARTNER_IDS } from './whitelistedPartnerIds';
|
|
7
|
+
export type { PartnerId, PartnerTheme } from './whitelistedPartnerIds';
|
package/src/config/url.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const HEADLESS_SDK_URL = 'https://
|
|
1
|
+
export const HEADLESS_SDK_URL = 'https://embedded-finance-frontend.vercel.app/npm/@wf-financing/headless@2';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Themes } from '@wayflyer/flyui';
|
|
2
|
+
|
|
3
|
+
export type PartnerTheme = Exclude<Themes, 'staff'>;
|
|
4
|
+
export type PartnerId = (typeof WHITELISTED_PARTNER_IDS)[PartnerTheme];
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* List of partner IDs that are allowed to use custom CTA themes
|
|
8
|
+
* when embedded into their platform.
|
|
9
|
+
*/
|
|
10
|
+
export const WHITELISTED_PARTNER_IDS = {
|
|
11
|
+
whiteLabel: '2416e2af-c9f7-40ac-a2f0-f37dcfe59674',
|
|
12
|
+
wayflyer: '1f309265-5324-4f8b-88d7-9b0d5c77a27c',
|
|
13
|
+
defaultTheme: 'defaultTheme',
|
|
14
|
+
} as const satisfies Record<PartnerTheme, string>;
|
|
@@ -4,9 +4,9 @@ import { continueHostedApplication } from '../api';
|
|
|
4
4
|
import { usePartnerContext } from './usePartnerContext';
|
|
5
5
|
|
|
6
6
|
export const useContinueHostedApplication = () => {
|
|
7
|
-
const { companyToken,
|
|
7
|
+
const { companyToken, options } = usePartnerContext();
|
|
8
8
|
|
|
9
9
|
return useMutation({
|
|
10
|
-
mutationFn: () => continueHostedApplication(companyToken,
|
|
10
|
+
mutationFn: () => continueHostedApplication(companyToken, options),
|
|
11
11
|
});
|
|
12
12
|
};
|
|
@@ -4,11 +4,11 @@ import { fetchCtaBanner } from '../api';
|
|
|
4
4
|
import { usePartnerContext } from './usePartnerContext';
|
|
5
5
|
|
|
6
6
|
export const useCtaBanner = () => {
|
|
7
|
-
const { companyToken,
|
|
7
|
+
const { companyToken, options } = usePartnerContext();
|
|
8
8
|
|
|
9
9
|
return useQuery({
|
|
10
|
-
queryKey: ['cta', companyToken,
|
|
11
|
-
queryFn: () => fetchCtaBanner(companyToken,
|
|
10
|
+
queryKey: ['cta', companyToken, options],
|
|
11
|
+
queryFn: () => fetchCtaBanner(companyToken, options),
|
|
12
12
|
staleTime: Infinity,
|
|
13
13
|
});
|
|
14
14
|
};
|
|
@@ -4,9 +4,9 @@ import { dismissCta } from '../api';
|
|
|
4
4
|
import { usePartnerContext } from './usePartnerContext';
|
|
5
5
|
|
|
6
6
|
export const useDismissCta = () => {
|
|
7
|
-
const { companyToken,
|
|
7
|
+
const { companyToken, options } = usePartnerContext();
|
|
8
8
|
|
|
9
9
|
return useMutation({
|
|
10
|
-
mutationFn: () => dismissCta(companyToken,
|
|
10
|
+
mutationFn: () => dismissCta(companyToken, options),
|
|
11
11
|
});
|
|
12
12
|
};
|
|
@@ -4,9 +4,9 @@ import { startHostedApplication } from '../api';
|
|
|
4
4
|
import { usePartnerContext } from './usePartnerContext';
|
|
5
5
|
|
|
6
6
|
export const useStartHostedApplication = () => {
|
|
7
|
-
const { companyToken,
|
|
7
|
+
const { companyToken, options, partnerCallback } = usePartnerContext();
|
|
8
8
|
|
|
9
9
|
return useMutation({
|
|
10
|
-
mutationFn: () => startHostedApplication(companyToken, partnerCallback,
|
|
10
|
+
mutationFn: () => startHostedApplication(companyToken, partnerCallback, options),
|
|
11
11
|
});
|
|
12
12
|
};
|
package/src/main.tsx
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { PartnerCallbackType, MockedModeType } from '@wf-financing/embedded-types';
|
|
1
|
+
import { PartnerCallbackType, SdkOptionsType } from '@wf-financing/embedded-types';
|
|
3
2
|
import ReactDOM from 'react-dom/client';
|
|
4
3
|
import { StyleSheetManager } from 'styled-components';
|
|
5
4
|
|
|
6
5
|
import { App } from './App';
|
|
7
|
-
import {
|
|
6
|
+
import { PartnerTheme } from './config';
|
|
7
|
+
import { applyFont, createRoots } from './utils';
|
|
8
8
|
|
|
9
9
|
let root: ReactDOM.Root | undefined = undefined;
|
|
10
10
|
|
|
11
11
|
export const mountToTarget = async (
|
|
12
12
|
targetId: string,
|
|
13
|
-
|
|
13
|
+
partnerTheme: PartnerTheme,
|
|
14
14
|
partnerCallback: PartnerCallbackType,
|
|
15
15
|
companyToken: string,
|
|
16
|
-
|
|
16
|
+
options?: SdkOptionsType,
|
|
17
17
|
): Promise<void> => {
|
|
18
18
|
const hostEl = document.getElementById(targetId);
|
|
19
19
|
if (!hostEl) throw new Error(`Target element with id "${targetId}" not found.`);
|
|
@@ -21,7 +21,7 @@ export const mountToTarget = async (
|
|
|
21
21
|
const shadow = hostEl.shadowRoot ?? hostEl.attachShadow({ mode: 'open' });
|
|
22
22
|
|
|
23
23
|
try {
|
|
24
|
-
await applyFont(shadow,
|
|
24
|
+
await applyFont(shadow, partnerTheme);
|
|
25
25
|
} catch (error) {
|
|
26
26
|
console.error(error);
|
|
27
27
|
}
|
|
@@ -39,9 +39,9 @@ export const mountToTarget = async (
|
|
|
39
39
|
root.render(
|
|
40
40
|
<StyleSheetManager target={styledComponentsStyleRoot}>
|
|
41
41
|
<App
|
|
42
|
-
|
|
42
|
+
theme={partnerTheme}
|
|
43
43
|
companyToken={companyToken}
|
|
44
|
-
|
|
44
|
+
options={options}
|
|
45
45
|
partnerCallback={partnerCallback}
|
|
46
46
|
onWidgetClose={handleCloseWidget}
|
|
47
47
|
portalContainer={portalRoot}
|
package/src/utils/applyFont.ts
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { FONT_PARAMS } from '../config';
|
|
1
|
+
import { FONT_PARAMS, PartnerTheme } from '../config';
|
|
4
2
|
import { loadFont } from './loadFont';
|
|
5
3
|
|
|
6
|
-
type ApplyFontsType = (shadow: ShadowRoot,
|
|
4
|
+
type ApplyFontsType = (shadow: ShadowRoot, partnerTheme: PartnerTheme) => Promise<void>;
|
|
7
5
|
|
|
8
|
-
export const applyFont: ApplyFontsType = async (shadow,
|
|
6
|
+
export const applyFont: ApplyFontsType = async (shadow, partnerTheme) => {
|
|
9
7
|
const { dmSansParams, merrionSansParams } = FONT_PARAMS;
|
|
10
8
|
|
|
11
|
-
switch (
|
|
9
|
+
switch (partnerTheme) {
|
|
12
10
|
case 'whiteLabel':
|
|
13
11
|
await loadFont(shadow, dmSansParams);
|
|
14
12
|
break;
|
|
15
13
|
case 'wayflyer':
|
|
16
|
-
case 'staff':
|
|
17
14
|
case 'defaultTheme':
|
|
18
15
|
await loadFont(shadow, merrionSansParams);
|
|
19
16
|
break;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { PartnerId } from '../config';
|
|
2
|
+
import { parseJwt } from './parseJwt';
|
|
3
|
+
|
|
4
|
+
export const getPartnerIdFromToken = (token: string): PartnerId => {
|
|
5
|
+
const company = parseJwt(token);
|
|
6
|
+
const partnerId = company?.sub.split('|')[0] as PartnerId;
|
|
7
|
+
|
|
8
|
+
return partnerId;
|
|
9
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { PartnerId, PartnerTheme, WHITELISTED_PARTNER_IDS } from '../config';
|
|
2
|
+
|
|
3
|
+
const PARTNER_BY_VALUE: Record<PartnerId, PartnerTheme> = Object.fromEntries(
|
|
4
|
+
Object.entries(WHITELISTED_PARTNER_IDS).map(([k, v]) => [v, k]),
|
|
5
|
+
) as Record<PartnerId, PartnerTheme>;
|
|
6
|
+
|
|
7
|
+
export const getPartnerThemeById = (value: PartnerId): PartnerTheme => {
|
|
8
|
+
return PARTNER_BY_VALUE[value] || 'whiteLabel';
|
|
9
|
+
};
|
package/src/utils/index.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
export { applyFont } from './applyFont';
|
|
2
|
+
export { createRoots } from './createRoots';
|
|
3
|
+
export { getPartnerIdFromToken } from './getPartnerIdFromToken';
|
|
4
|
+
export { getPartnerThemeById } from './getPartnerThemeById';
|
|
1
5
|
export { initializeHeadlessSdk } from './initializeHeadlessSdk';
|
|
2
6
|
export { loadScriptAndInitializeSdk } from './loadScriptAndInitializeSdk';
|
|
3
7
|
export { PartnerContext, type PartnerContextType } from './partnerContext';
|
|
4
|
-
export { createRoots } from './createRoots';
|
|
5
|
-
export { applyFont } from './applyFont';
|
|
@@ -1,18 +1,14 @@
|
|
|
1
|
-
import { IHeadlessWayflyerSdk,
|
|
1
|
+
import { IHeadlessWayflyerSdk, SdkOptionsType } from '@wf-financing/embedded-types';
|
|
2
2
|
|
|
3
|
-
export const initializeHeadlessSdk = (companyToken: string,
|
|
3
|
+
export const initializeHeadlessSdk = (companyToken: string, options?: SdkOptionsType): IHeadlessWayflyerSdk => {
|
|
4
4
|
if (!window.WayflyerHeadlessSdk) {
|
|
5
5
|
throw new Error('Failed to load WayflyerHeadlessSdk from the script.');
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
const WayflyerHeadlessSdk = window.WayflyerHeadlessSdk;
|
|
9
9
|
|
|
10
|
-
if (
|
|
11
|
-
|
|
12
|
-
const { isMockedMode, sdkScenario } = mockedMode;
|
|
13
|
-
|
|
14
|
-
const wayflyerCtaSdk = new WayflyerHeadlessSdk(companyToken, isMockedMode);
|
|
15
|
-
wayflyerCtaSdk.setSdkScenario(sdkScenario);
|
|
10
|
+
if (options) {
|
|
11
|
+
const wayflyerCtaSdk = new WayflyerHeadlessSdk(companyToken, options);
|
|
16
12
|
|
|
17
13
|
return wayflyerCtaSdk;
|
|
18
14
|
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { IHeadlessWayflyerSdk,
|
|
1
|
+
import { IHeadlessWayflyerSdk, SdkOptionsType } from '@wf-financing/embedded-types';
|
|
2
2
|
|
|
3
3
|
import { initializeHeadlessSdk } from './initializeHeadlessSdk';
|
|
4
4
|
|
|
5
5
|
export const loadScriptAndInitializeSdk = (
|
|
6
6
|
script: HTMLScriptElement,
|
|
7
7
|
companyToken: string,
|
|
8
|
-
|
|
8
|
+
options?: SdkOptionsType,
|
|
9
9
|
): Promise<IHeadlessWayflyerSdk> => {
|
|
10
10
|
return new Promise<IHeadlessWayflyerSdk>((resolve, reject) => {
|
|
11
11
|
script.onload = () => {
|
|
12
12
|
try {
|
|
13
|
-
resolve(initializeHeadlessSdk(companyToken,
|
|
13
|
+
resolve(initializeHeadlessSdk(companyToken, options));
|
|
14
14
|
} catch (error) {
|
|
15
15
|
reject(error);
|
|
16
16
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type CompanyToken = {
|
|
2
|
+
sub: string;
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
export const parseJwt = (token: string): CompanyToken | null => {
|
|
6
|
+
try {
|
|
7
|
+
return JSON.parse(atob(token.split('.')[1]));
|
|
8
|
+
} catch (error) {
|
|
9
|
+
console.error('Error parsing a company token:', error);
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
};
|