@frak-labs/react-sdk 0.1.1 → 0.2.0-beta.7898df5b
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/README.md +25 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +29 -68
- package/dist/index.d.ts +28 -67
- package/dist/index.js +1 -1
- package/package.json +17 -16
- package/src/hook/helper/useReferralInteraction.test.ts +309 -0
- package/src/hook/helper/useReferralInteraction.ts +73 -0
- package/src/hook/index.ts +10 -0
- package/src/hook/useDisplayModal.test.ts +275 -0
- package/src/hook/useDisplayModal.ts +68 -0
- package/src/hook/useFrakClient.test.ts +119 -0
- package/src/hook/useFrakClient.ts +11 -0
- package/src/hook/useFrakConfig.test.ts +184 -0
- package/src/hook/useFrakConfig.ts +22 -0
- package/src/hook/useGetMerchantInformation.ts +56 -0
- package/src/hook/useOpenSso.test.ts +202 -0
- package/src/hook/useOpenSso.ts +51 -0
- package/src/hook/usePrepareSso.test.ts +197 -0
- package/src/hook/usePrepareSso.ts +55 -0
- package/src/hook/useSendTransaction.test.ts +218 -0
- package/src/hook/useSendTransaction.ts +62 -0
- package/src/hook/useSiweAuthenticate.test.ts +258 -0
- package/src/hook/useSiweAuthenticate.ts +66 -0
- package/src/hook/useWalletStatus.test.ts +112 -0
- package/src/hook/useWalletStatus.ts +55 -0
- package/src/hook/utils/useFrakContext.test.ts +157 -0
- package/src/hook/utils/useFrakContext.ts +42 -0
- package/src/hook/utils/useMounted.test.ts +70 -0
- package/src/hook/utils/useMounted.ts +12 -0
- package/src/hook/utils/useWindowLocation.test.ts +54 -0
- package/src/hook/utils/useWindowLocation.ts +40 -0
- package/src/index.ts +25 -0
- package/src/provider/FrakConfigProvider.test.ts +246 -0
- package/src/provider/FrakConfigProvider.ts +54 -0
- package/src/provider/FrakIFrameClientProvider.test.tsx +209 -0
- package/src/provider/FrakIFrameClientProvider.ts +86 -0
- package/src/provider/index.ts +7 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for FrakIFrameClientProvider
|
|
3
|
+
* Tests iframe creation and FrakClient provider
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { vi } from "vitest";
|
|
7
|
+
|
|
8
|
+
vi.mock("@frak-labs/core-sdk", async () => {
|
|
9
|
+
const actual = await vi.importActual<typeof import("@frak-labs/core-sdk")>(
|
|
10
|
+
"@frak-labs/core-sdk"
|
|
11
|
+
);
|
|
12
|
+
return {
|
|
13
|
+
...actual,
|
|
14
|
+
createIFrameFrakClient: vi.fn(),
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
import type { FrakClient } from "@frak-labs/core-sdk";
|
|
19
|
+
import { createIFrameFrakClient } from "@frak-labs/core-sdk";
|
|
20
|
+
import { render, waitFor } from "@testing-library/react";
|
|
21
|
+
import { createElement, type ReactNode } from "react";
|
|
22
|
+
import { describe, expect, test } from "../../tests/vitest-fixtures";
|
|
23
|
+
import { FrakConfigProvider } from "./FrakConfigProvider";
|
|
24
|
+
import { FrakIFrameClientProvider } from "./FrakIFrameClientProvider";
|
|
25
|
+
|
|
26
|
+
describe("FrakIFrameClientProvider", () => {
|
|
27
|
+
test("should render iframe with correct src", ({ mockFrakConfig }) => {
|
|
28
|
+
const Wrapper = ({ children }: { children: ReactNode }) =>
|
|
29
|
+
createElement(
|
|
30
|
+
FrakConfigProvider,
|
|
31
|
+
{ config: mockFrakConfig },
|
|
32
|
+
children
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
render(createElement(FrakIFrameClientProvider), {
|
|
36
|
+
wrapper: Wrapper,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const iframe = document.querySelector("iframe");
|
|
40
|
+
expect(iframe).toBeDefined();
|
|
41
|
+
expect(iframe?.src).toContain(`${mockFrakConfig.walletUrl}/listener`);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("should apply custom styles to iframe", ({ mockFrakConfig }) => {
|
|
45
|
+
const Wrapper = ({ children }: { children: ReactNode }) =>
|
|
46
|
+
createElement(
|
|
47
|
+
FrakConfigProvider,
|
|
48
|
+
{ config: mockFrakConfig },
|
|
49
|
+
children
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const customStyle = {
|
|
53
|
+
width: "500px",
|
|
54
|
+
height: "600px",
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
render(
|
|
58
|
+
createElement(FrakIFrameClientProvider, { style: customStyle }),
|
|
59
|
+
{
|
|
60
|
+
wrapper: Wrapper,
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const iframe = document.querySelector("iframe");
|
|
65
|
+
expect(iframe?.style.width).toBe("500px");
|
|
66
|
+
expect(iframe?.style.height).toBe("600px");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("should create FrakClient when iframe ref is set", async ({
|
|
70
|
+
mockFrakConfig,
|
|
71
|
+
}) => {
|
|
72
|
+
const Wrapper = ({ children }: { children: ReactNode }) =>
|
|
73
|
+
createElement(
|
|
74
|
+
FrakConfigProvider,
|
|
75
|
+
{ config: mockFrakConfig },
|
|
76
|
+
children
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const mockClient = { config: mockFrakConfig } as FrakClient;
|
|
80
|
+
vi.mocked(createIFrameFrakClient).mockReturnValue(mockClient);
|
|
81
|
+
|
|
82
|
+
render(createElement(FrakIFrameClientProvider), {
|
|
83
|
+
wrapper: Wrapper,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
await waitFor(() => {
|
|
87
|
+
expect(createIFrameFrakClient).toHaveBeenCalled();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("should not recreate client if already exists", async ({
|
|
92
|
+
mockFrakConfig,
|
|
93
|
+
}) => {
|
|
94
|
+
const Wrapper = ({ children }: { children: ReactNode }) =>
|
|
95
|
+
createElement(
|
|
96
|
+
FrakConfigProvider,
|
|
97
|
+
{ config: mockFrakConfig },
|
|
98
|
+
children
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const mockClient = { config: mockFrakConfig } as FrakClient;
|
|
102
|
+
vi.mocked(createIFrameFrakClient).mockReturnValue(mockClient);
|
|
103
|
+
|
|
104
|
+
const { rerender } = render(createElement(FrakIFrameClientProvider), {
|
|
105
|
+
wrapper: Wrapper,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
await waitFor(() => {
|
|
109
|
+
expect(createIFrameFrakClient).toHaveBeenCalledTimes(1);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Rerender the component
|
|
113
|
+
rerender(createElement(FrakIFrameClientProvider));
|
|
114
|
+
|
|
115
|
+
// Should still only be called once
|
|
116
|
+
expect(createIFrameFrakClient).toHaveBeenCalledTimes(1);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("should render without children", ({ mockFrakConfig }) => {
|
|
120
|
+
const Wrapper = ({ children }: { children: ReactNode }) =>
|
|
121
|
+
createElement(
|
|
122
|
+
FrakConfigProvider,
|
|
123
|
+
{ config: mockFrakConfig },
|
|
124
|
+
children
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const { container } = render(createElement(FrakIFrameClientProvider), {
|
|
128
|
+
wrapper: Wrapper,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const iframe = container.querySelector("iframe");
|
|
132
|
+
expect(iframe).toBeDefined();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("should use baseIframeProps for iframe attributes", ({
|
|
136
|
+
mockFrakConfig,
|
|
137
|
+
}) => {
|
|
138
|
+
const Wrapper = ({ children }: { children: ReactNode }) =>
|
|
139
|
+
createElement(
|
|
140
|
+
FrakConfigProvider,
|
|
141
|
+
{ config: mockFrakConfig },
|
|
142
|
+
children
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
render(createElement(FrakIFrameClientProvider), {
|
|
146
|
+
wrapper: Wrapper,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const iframe = document.querySelector("iframe");
|
|
150
|
+
expect(iframe).toBeDefined();
|
|
151
|
+
// baseIframeProps should set these attributes
|
|
152
|
+
expect(iframe?.getAttribute("sandbox")).toBeDefined();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("should handle iframe ref callback correctly", ({
|
|
156
|
+
mockFrakConfig,
|
|
157
|
+
}) => {
|
|
158
|
+
const Wrapper = ({ children }: { children: ReactNode }) =>
|
|
159
|
+
createElement(
|
|
160
|
+
FrakConfigProvider,
|
|
161
|
+
{ config: mockFrakConfig },
|
|
162
|
+
children
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
const mockClient = { config: mockFrakConfig } as FrakClient;
|
|
166
|
+
let callCount = 0;
|
|
167
|
+
|
|
168
|
+
vi.mocked(createIFrameFrakClient).mockImplementation(() => {
|
|
169
|
+
callCount++;
|
|
170
|
+
return mockClient;
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
render(createElement(FrakIFrameClientProvider), {
|
|
174
|
+
wrapper: Wrapper,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Client creation should be called at most once
|
|
178
|
+
expect(callCount).toBeLessThanOrEqual(1);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("should pass config to createIFrameFrakClient", async ({
|
|
182
|
+
mockFrakConfig,
|
|
183
|
+
}) => {
|
|
184
|
+
const Wrapper = ({ children }: { children: ReactNode }) =>
|
|
185
|
+
createElement(
|
|
186
|
+
FrakConfigProvider,
|
|
187
|
+
{ config: mockFrakConfig },
|
|
188
|
+
children
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
const mockClient = { config: mockFrakConfig } as FrakClient;
|
|
192
|
+
vi.mocked(createIFrameFrakClient).mockReturnValue(mockClient);
|
|
193
|
+
|
|
194
|
+
render(createElement(FrakIFrameClientProvider), {
|
|
195
|
+
wrapper: Wrapper,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
await waitFor(() => {
|
|
199
|
+
expect(createIFrameFrakClient).toHaveBeenCalledWith(
|
|
200
|
+
expect.objectContaining({
|
|
201
|
+
config: expect.objectContaining({
|
|
202
|
+
domain: "example.com",
|
|
203
|
+
walletUrl: "https://wallet-test.frak.id",
|
|
204
|
+
}),
|
|
205
|
+
})
|
|
206
|
+
);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import {
|
|
2
|
+
baseIframeProps,
|
|
3
|
+
createIFrameFrakClient,
|
|
4
|
+
type FrakClient,
|
|
5
|
+
type FrakWalletSdkConfig,
|
|
6
|
+
} from "@frak-labs/core-sdk";
|
|
7
|
+
import {
|
|
8
|
+
type CSSProperties,
|
|
9
|
+
createContext,
|
|
10
|
+
createElement,
|
|
11
|
+
Fragment,
|
|
12
|
+
type ReactNode,
|
|
13
|
+
useState,
|
|
14
|
+
} from "react";
|
|
15
|
+
import { useFrakConfig } from "../hook";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The context that will keep the Frak Wallet SDK client
|
|
19
|
+
* @ignore
|
|
20
|
+
*/
|
|
21
|
+
export const FrakIFrameClientContext = createContext<FrakClient | undefined>(
|
|
22
|
+
undefined
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Props to instantiate the Frak Wallet SDK configuration provider
|
|
27
|
+
*
|
|
28
|
+
* @group provider
|
|
29
|
+
*/
|
|
30
|
+
export type FrakIFrameClientProps = {
|
|
31
|
+
config: FrakWalletSdkConfig;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* IFrame client provider for the Frak Wallet SDK
|
|
36
|
+
* It will automatically create the frak wallet iFrame (required for the wallet to communicate with the SDK securely), and provide it in the context
|
|
37
|
+
*
|
|
38
|
+
* @group provider
|
|
39
|
+
*
|
|
40
|
+
* @remarks
|
|
41
|
+
* This provider must be wrapped within a {@link FrakConfigProvider} to work properly
|
|
42
|
+
*
|
|
43
|
+
* @param args
|
|
44
|
+
* @param args.style - Some custom styles to apply to the iFrame
|
|
45
|
+
* @param args.children - Descedant components that will have access to the Frak Client
|
|
46
|
+
*/
|
|
47
|
+
export function FrakIFrameClientProvider({
|
|
48
|
+
style,
|
|
49
|
+
children,
|
|
50
|
+
}: {
|
|
51
|
+
style?: CSSProperties;
|
|
52
|
+
children?: ReactNode;
|
|
53
|
+
}) {
|
|
54
|
+
const config = useFrakConfig();
|
|
55
|
+
|
|
56
|
+
// Using a state for the client since using directly a client built inside the ref cause re-render loop
|
|
57
|
+
const [client, setClient] = useState<FrakClient | undefined>(undefined);
|
|
58
|
+
|
|
59
|
+
// Create the iframe that will be used to communicate with the wallet
|
|
60
|
+
const iFrame = createElement("iframe", {
|
|
61
|
+
...baseIframeProps,
|
|
62
|
+
src: `${config.walletUrl}/listener`,
|
|
63
|
+
style: style ?? baseIframeProps.style,
|
|
64
|
+
ref: (iframe: HTMLIFrameElement) => {
|
|
65
|
+
if (!iframe || client) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
setClient(
|
|
69
|
+
createIFrameFrakClient({
|
|
70
|
+
iframe,
|
|
71
|
+
config,
|
|
72
|
+
})
|
|
73
|
+
);
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Create the component that will provide the client
|
|
78
|
+
const providerComponent = createElement(
|
|
79
|
+
FrakIFrameClientContext.Provider,
|
|
80
|
+
{ value: client },
|
|
81
|
+
children
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
// Return both components
|
|
85
|
+
return createElement(Fragment, null, iFrame, providerComponent);
|
|
86
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type { FrakConfigProviderProps } from "./FrakConfigProvider";
|
|
2
|
+
export { FrakConfigContext, FrakConfigProvider } from "./FrakConfigProvider";
|
|
3
|
+
export type { FrakIFrameClientProps } from "./FrakIFrameClientProvider";
|
|
4
|
+
export {
|
|
5
|
+
FrakIFrameClientContext,
|
|
6
|
+
FrakIFrameClientProvider,
|
|
7
|
+
} from "./FrakIFrameClientProvider";
|