@frak-labs/react-sdk 0.1.1 → 0.2.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/README.md +25 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +28 -64
- package/dist/index.d.ts +28 -64
- package/dist/index.js +1 -1
- package/package.json +17 -16
- package/src/hook/helper/useReferralInteraction.test.ts +358 -0
- package/src/hook/helper/useReferralInteraction.ts +78 -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,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for useWalletStatus hook
|
|
3
|
+
* Tests TanStack Query wrapper for watching wallet status
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { vi } from "vitest";
|
|
7
|
+
|
|
8
|
+
vi.mock("@frak-labs/core-sdk/actions");
|
|
9
|
+
|
|
10
|
+
import type { WalletStatusReturnType } from "@frak-labs/core-sdk";
|
|
11
|
+
import { watchWalletStatus } from "@frak-labs/core-sdk/actions";
|
|
12
|
+
import { renderHook, waitFor } from "@testing-library/react";
|
|
13
|
+
import { describe, expect, test } from "../../tests/vitest-fixtures";
|
|
14
|
+
import { useWalletStatus } from "./useWalletStatus";
|
|
15
|
+
|
|
16
|
+
describe("useWalletStatus", () => {
|
|
17
|
+
test("should be disabled when client is not available", ({
|
|
18
|
+
queryWrapper,
|
|
19
|
+
}) => {
|
|
20
|
+
const { result } = renderHook(() => useWalletStatus(), {
|
|
21
|
+
wrapper: queryWrapper.wrapper,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Query should not run when client is not available
|
|
25
|
+
expect(result.current.isPending).toBe(true);
|
|
26
|
+
expect(result.current.isFetching).toBe(false);
|
|
27
|
+
expect(result.current.data).toBeUndefined();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("should watch wallet status successfully", async ({
|
|
31
|
+
mockFrakProviders,
|
|
32
|
+
}) => {
|
|
33
|
+
const mockStatus: WalletStatusReturnType = {
|
|
34
|
+
key: "connected",
|
|
35
|
+
wallet: "0x1234567890123456789012345678901234567890",
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
vi.mocked(watchWalletStatus).mockResolvedValue(mockStatus);
|
|
39
|
+
|
|
40
|
+
const { result } = renderHook(() => useWalletStatus(), {
|
|
41
|
+
wrapper: mockFrakProviders,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
await waitFor(() => {
|
|
45
|
+
expect(result.current.isSuccess).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
expect(result.current.data).toEqual(mockStatus);
|
|
49
|
+
expect(watchWalletStatus).toHaveBeenCalledTimes(1);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("should return not connected status", async ({
|
|
53
|
+
mockFrakProviders,
|
|
54
|
+
}) => {
|
|
55
|
+
const mockStatus: WalletStatusReturnType = {
|
|
56
|
+
key: "not-connected",
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
vi.mocked(watchWalletStatus).mockResolvedValue(mockStatus);
|
|
60
|
+
|
|
61
|
+
const { result } = renderHook(() => useWalletStatus(), {
|
|
62
|
+
wrapper: mockFrakProviders,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
await waitFor(() => {
|
|
66
|
+
expect(result.current.isSuccess).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
expect(result.current.data).toEqual(mockStatus);
|
|
70
|
+
expect(result.current.data?.key).toBe("not-connected");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("should handle RPC errors", async ({ mockFrakProviders }) => {
|
|
74
|
+
const error = new Error("Wallet status watch failed");
|
|
75
|
+
vi.mocked(watchWalletStatus).mockRejectedValue(error);
|
|
76
|
+
|
|
77
|
+
const { result } = renderHook(() => useWalletStatus(), {
|
|
78
|
+
wrapper: mockFrakProviders,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
await waitFor(() => {
|
|
82
|
+
expect(result.current.isError).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
expect(result.current.error).toEqual(error);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("should pass callback to watchWalletStatus", async ({
|
|
89
|
+
mockFrakProviders,
|
|
90
|
+
mockFrakClient,
|
|
91
|
+
}) => {
|
|
92
|
+
const mockStatus: WalletStatusReturnType = {
|
|
93
|
+
key: "connected",
|
|
94
|
+
wallet: "0x1234567890123456789012345678901234567890",
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
vi.mocked(watchWalletStatus).mockResolvedValue(mockStatus);
|
|
98
|
+
|
|
99
|
+
const { result } = renderHook(() => useWalletStatus(), {
|
|
100
|
+
wrapper: mockFrakProviders,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
await waitFor(() => {
|
|
104
|
+
expect(result.current.isSuccess).toBe(true);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
expect(watchWalletStatus).toHaveBeenCalledWith(
|
|
108
|
+
mockFrakClient,
|
|
109
|
+
expect.any(Function)
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { WalletStatusReturnType } from "@frak-labs/core-sdk";
|
|
2
|
+
import { watchWalletStatus } from "@frak-labs/core-sdk/actions";
|
|
3
|
+
import { ClientNotFound } from "@frak-labs/frame-connector";
|
|
4
|
+
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
5
|
+
import { useCallback } from "react";
|
|
6
|
+
import { useFrakClient } from "./useFrakClient";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Hook that return a query helping to get the current wallet status.
|
|
10
|
+
*
|
|
11
|
+
* It's a {@link @tanstack/react-query!home | `tanstack`} wrapper around the {@link @frak-labs/core-sdk!actions.watchWalletStatus | `watchWalletStatus()`} action
|
|
12
|
+
*
|
|
13
|
+
* @group hooks
|
|
14
|
+
*
|
|
15
|
+
* @returns
|
|
16
|
+
* The query hook wrapping the `watchWalletStatus()` action
|
|
17
|
+
* The `data` result is a {@link @frak-labs/core-sdk!index.WalletStatusReturnType | `WalletStatusReturnType`}
|
|
18
|
+
*
|
|
19
|
+
* @see {@link @frak-labs/core-sdk!actions.watchWalletStatus | `watchWalletStatus()`} for more info about the underlying action
|
|
20
|
+
* @see {@link @tanstack/react-query!useQuery | `useQuery()`} for more info about the useQuery response
|
|
21
|
+
*/
|
|
22
|
+
export function useWalletStatus() {
|
|
23
|
+
const queryClient = useQueryClient();
|
|
24
|
+
const client = useFrakClient();
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Callback hook when we receive an updated wallet status
|
|
28
|
+
*/
|
|
29
|
+
const newStatusUpdated = useCallback(
|
|
30
|
+
(event: WalletStatusReturnType) => {
|
|
31
|
+
queryClient.setQueryData(
|
|
32
|
+
["frak-sdk", "wallet-status-listener"],
|
|
33
|
+
event
|
|
34
|
+
);
|
|
35
|
+
},
|
|
36
|
+
[queryClient]
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Setup the query listener
|
|
41
|
+
*/
|
|
42
|
+
return useQuery<WalletStatusReturnType>({
|
|
43
|
+
gcTime: 0,
|
|
44
|
+
staleTime: 0,
|
|
45
|
+
queryKey: ["frak-sdk", "wallet-status-listener"],
|
|
46
|
+
queryFn: async () => {
|
|
47
|
+
if (!client) {
|
|
48
|
+
throw new ClientNotFound();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return watchWalletStatus(client, newStatusUpdated);
|
|
52
|
+
},
|
|
53
|
+
enabled: !!client,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for useFrakContext hook
|
|
3
|
+
* Tests hook that extracts and manages Frak context from URL
|
|
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
|
+
FrakContextManager: {
|
|
15
|
+
parse: vi.fn(),
|
|
16
|
+
replaceUrl: vi.fn(),
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
vi.mock("./useWindowLocation");
|
|
22
|
+
|
|
23
|
+
import type { FrakContext } from "@frak-labs/core-sdk";
|
|
24
|
+
import { FrakContextManager } from "@frak-labs/core-sdk";
|
|
25
|
+
import { renderHook } from "@testing-library/react";
|
|
26
|
+
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
|
27
|
+
import { useFrakContext } from "./useFrakContext";
|
|
28
|
+
import { useWindowLocation } from "./useWindowLocation";
|
|
29
|
+
|
|
30
|
+
describe("useFrakContext", () => {
|
|
31
|
+
const consoleLogSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
|
32
|
+
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
vi.clearAllMocks();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
consoleLogSpy.mockClear();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("should return null when no location href", () => {
|
|
42
|
+
vi.mocked(useWindowLocation).mockReturnValue({
|
|
43
|
+
location: undefined,
|
|
44
|
+
href: undefined,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const { result } = renderHook(() => useFrakContext());
|
|
48
|
+
|
|
49
|
+
expect(result.current.frakContext).toBeNull();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("should parse frak context from URL", () => {
|
|
53
|
+
const mockContext: FrakContext = {
|
|
54
|
+
r: "0x1234567890123456789012345678901234567890",
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
vi.mocked(useWindowLocation).mockReturnValue({
|
|
58
|
+
location: { href: "https://example.com?frak=test" } as Location,
|
|
59
|
+
href: "https://example.com?frak=test",
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
vi.mocked(FrakContextManager.parse).mockReturnValue(mockContext);
|
|
63
|
+
|
|
64
|
+
const { result } = renderHook(() => useFrakContext());
|
|
65
|
+
|
|
66
|
+
expect(FrakContextManager.parse).toHaveBeenCalledWith({
|
|
67
|
+
url: "https://example.com?frak=test",
|
|
68
|
+
});
|
|
69
|
+
expect(result.current.frakContext).toEqual(mockContext);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("should update context with new values", () => {
|
|
73
|
+
vi.mocked(useWindowLocation).mockReturnValue({
|
|
74
|
+
location: { href: "https://example.com" } as Location,
|
|
75
|
+
href: "https://example.com",
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
vi.mocked(FrakContextManager.parse).mockReturnValue(null);
|
|
79
|
+
|
|
80
|
+
const { result } = renderHook(() => useFrakContext());
|
|
81
|
+
|
|
82
|
+
const newContext: Partial<FrakContext> = {
|
|
83
|
+
r: "0x4567890123456789012345678901234567890123",
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
result.current.updateContext(newContext);
|
|
87
|
+
|
|
88
|
+
expect(console.log).toHaveBeenCalledWith("Updating context", {
|
|
89
|
+
newContext,
|
|
90
|
+
});
|
|
91
|
+
expect(FrakContextManager.replaceUrl).toHaveBeenCalledWith({
|
|
92
|
+
url: "https://example.com",
|
|
93
|
+
context: newContext,
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("should memoize frak context based on href", () => {
|
|
98
|
+
const mockContext: FrakContext = {
|
|
99
|
+
r: "0x7890123456789012345678901234567890123456",
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
vi.mocked(useWindowLocation).mockReturnValue({
|
|
103
|
+
location: { href: "https://example.com?test=1" } as Location,
|
|
104
|
+
href: "https://example.com?test=1",
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
vi.mocked(FrakContextManager.parse).mockReturnValue(mockContext);
|
|
108
|
+
|
|
109
|
+
const { result, rerender } = renderHook(() => useFrakContext());
|
|
110
|
+
|
|
111
|
+
const firstContext = result.current.frakContext;
|
|
112
|
+
|
|
113
|
+
// Rerender without changing href
|
|
114
|
+
rerender();
|
|
115
|
+
|
|
116
|
+
expect(result.current.frakContext).toBe(firstContext);
|
|
117
|
+
expect(FrakContextManager.parse).toHaveBeenCalledTimes(1);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("should reparse context when href changes", () => {
|
|
121
|
+
const mockContext1: FrakContext = {
|
|
122
|
+
r: "0x1111111111111111111111111111111111111111",
|
|
123
|
+
};
|
|
124
|
+
const mockContext2: FrakContext = {
|
|
125
|
+
r: "0x2222222222222222222222222222222222222222",
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const { rerender } = renderHook(() => useFrakContext());
|
|
129
|
+
|
|
130
|
+
vi.mocked(useWindowLocation).mockReturnValue({
|
|
131
|
+
location: { href: "https://example.com?v=1" } as Location,
|
|
132
|
+
href: "https://example.com?v=1",
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
vi.mocked(FrakContextManager.parse).mockReturnValue(mockContext1);
|
|
136
|
+
|
|
137
|
+
rerender();
|
|
138
|
+
|
|
139
|
+
expect(FrakContextManager.parse).toHaveBeenCalledWith({
|
|
140
|
+
url: "https://example.com?v=1",
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Change href
|
|
144
|
+
vi.mocked(useWindowLocation).mockReturnValue({
|
|
145
|
+
location: { href: "https://example.com?v=2" } as Location,
|
|
146
|
+
href: "https://example.com?v=2",
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
vi.mocked(FrakContextManager.parse).mockReturnValue(mockContext2);
|
|
150
|
+
|
|
151
|
+
rerender();
|
|
152
|
+
|
|
153
|
+
expect(FrakContextManager.parse).toHaveBeenCalledWith({
|
|
154
|
+
url: "https://example.com?v=2",
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type FrakContext, FrakContextManager } from "@frak-labs/core-sdk";
|
|
2
|
+
import { useCallback, useMemo } from "react";
|
|
3
|
+
import { useWindowLocation } from "./useWindowLocation";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Extract the current frak context from the url
|
|
7
|
+
* @ignore
|
|
8
|
+
*/
|
|
9
|
+
export function useFrakContext() {
|
|
10
|
+
// Get the current window location
|
|
11
|
+
const { location } = useWindowLocation();
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Fetching and parsing the current frak context
|
|
15
|
+
*/
|
|
16
|
+
const frakContext = useMemo(() => {
|
|
17
|
+
// If no url extracted yet, early exit
|
|
18
|
+
if (!location?.href) return null;
|
|
19
|
+
|
|
20
|
+
// Parse the current context
|
|
21
|
+
return FrakContextManager.parse({ url: location.href });
|
|
22
|
+
}, [location?.href]);
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Update the current context
|
|
26
|
+
*/
|
|
27
|
+
const updateContext = useCallback(
|
|
28
|
+
(newContext: Partial<FrakContext>) => {
|
|
29
|
+
console.log("Updating context", { newContext });
|
|
30
|
+
FrakContextManager.replaceUrl({
|
|
31
|
+
url: location?.href,
|
|
32
|
+
context: newContext,
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
[location?.href]
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
frakContext,
|
|
40
|
+
updateContext,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for useMounted hook
|
|
3
|
+
* Tests that the hook correctly tracks component mount state
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { renderHook, waitFor } from "@testing-library/react";
|
|
7
|
+
import { describe, expect, it } from "../../../tests/vitest-fixtures";
|
|
8
|
+
import { useMounted } from "./useMounted";
|
|
9
|
+
|
|
10
|
+
describe("useMounted", () => {
|
|
11
|
+
it("should return true after mount", async () => {
|
|
12
|
+
const { result } = renderHook(() => useMounted());
|
|
13
|
+
|
|
14
|
+
// In React Testing Library, effects run synchronously after render
|
|
15
|
+
// So by the time we check result.current, the effect has already run
|
|
16
|
+
await waitFor(() => {
|
|
17
|
+
expect(result.current).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("should remain true after multiple re-renders", async () => {
|
|
22
|
+
const { result, rerender } = renderHook(() => useMounted());
|
|
23
|
+
|
|
24
|
+
// Wait for initial mount
|
|
25
|
+
await waitFor(() => {
|
|
26
|
+
expect(result.current).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Re-render multiple times
|
|
30
|
+
rerender();
|
|
31
|
+
expect(result.current).toBe(true);
|
|
32
|
+
|
|
33
|
+
rerender();
|
|
34
|
+
expect(result.current).toBe(true);
|
|
35
|
+
|
|
36
|
+
rerender();
|
|
37
|
+
expect(result.current).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("should be stable across re-renders", async () => {
|
|
41
|
+
const { result, rerender } = renderHook(() => useMounted());
|
|
42
|
+
|
|
43
|
+
// Wait for mount
|
|
44
|
+
await waitFor(() => {
|
|
45
|
+
expect(result.current).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const firstValue = result.current;
|
|
49
|
+
|
|
50
|
+
// Re-render
|
|
51
|
+
rerender();
|
|
52
|
+
const secondValue = result.current;
|
|
53
|
+
|
|
54
|
+
// Values should be the same
|
|
55
|
+
expect(firstValue).toBe(secondValue);
|
|
56
|
+
expect(firstValue).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should handle unmounting gracefully", async () => {
|
|
60
|
+
const { result, unmount } = renderHook(() => useMounted());
|
|
61
|
+
|
|
62
|
+
// Wait for mount
|
|
63
|
+
await waitFor(() => {
|
|
64
|
+
expect(result.current).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Unmount should not throw
|
|
68
|
+
expect(() => unmount()).not.toThrow();
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for useWindowLocation hook
|
|
3
|
+
* Tests hook that tracks window.location changes
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { renderHook } from "@testing-library/react";
|
|
7
|
+
import { describe, expect, test, vi } from "vitest";
|
|
8
|
+
import { useWindowLocation } from "./useWindowLocation";
|
|
9
|
+
|
|
10
|
+
describe("useWindowLocation", () => {
|
|
11
|
+
test("should return current window location", () => {
|
|
12
|
+
const { result } = renderHook(() => useWindowLocation());
|
|
13
|
+
|
|
14
|
+
expect(result.current.location).toBeDefined();
|
|
15
|
+
expect(result.current.href).toBeDefined();
|
|
16
|
+
expect(typeof result.current.href).toBe("string");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("should derive href from location", () => {
|
|
20
|
+
const { result } = renderHook(() => useWindowLocation());
|
|
21
|
+
|
|
22
|
+
if (result.current.location) {
|
|
23
|
+
expect(result.current.href).toBe(result.current.location.href);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("should register popstate listener", () => {
|
|
28
|
+
const addEventListenerSpy = vi.spyOn(window, "addEventListener");
|
|
29
|
+
|
|
30
|
+
renderHook(() => useWindowLocation());
|
|
31
|
+
|
|
32
|
+
expect(addEventListenerSpy).toHaveBeenCalledWith(
|
|
33
|
+
"popstate",
|
|
34
|
+
expect.any(Function)
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
addEventListenerSpy.mockRestore();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("should cleanup popstate listener on unmount", () => {
|
|
41
|
+
const removeEventListenerSpy = vi.spyOn(window, "removeEventListener");
|
|
42
|
+
|
|
43
|
+
const { unmount } = renderHook(() => useWindowLocation());
|
|
44
|
+
|
|
45
|
+
unmount();
|
|
46
|
+
|
|
47
|
+
expect(removeEventListenerSpy).toHaveBeenCalledWith(
|
|
48
|
+
"popstate",
|
|
49
|
+
expect.any(Function)
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
removeEventListenerSpy.mockRestore();
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { useMounted } from "./useMounted";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns the current window location and href
|
|
6
|
+
* @ignore
|
|
7
|
+
*/
|
|
8
|
+
export function useWindowLocation(): {
|
|
9
|
+
location: Location | undefined;
|
|
10
|
+
href: string | undefined;
|
|
11
|
+
} {
|
|
12
|
+
const isMounted = useMounted();
|
|
13
|
+
const [location, setLocation] = useState<Location | undefined>(
|
|
14
|
+
isMounted ? window.location : undefined
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (!isMounted) return;
|
|
19
|
+
|
|
20
|
+
// Method to the current window location
|
|
21
|
+
function setWindowLocation() {
|
|
22
|
+
setLocation(window.location);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!location) {
|
|
26
|
+
setWindowLocation();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
window.addEventListener("popstate", setWindowLocation);
|
|
30
|
+
|
|
31
|
+
return () => {
|
|
32
|
+
window.removeEventListener("popstate", setWindowLocation);
|
|
33
|
+
};
|
|
34
|
+
}, [isMounted, location]);
|
|
35
|
+
|
|
36
|
+
// Derive the href from the location
|
|
37
|
+
const href = useMemo(() => location?.href, [location?.href]);
|
|
38
|
+
|
|
39
|
+
return { location, href };
|
|
40
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Providers export
|
|
2
|
+
|
|
3
|
+
// Hooks export
|
|
4
|
+
export {
|
|
5
|
+
useDisplayModal,
|
|
6
|
+
useFrakClient,
|
|
7
|
+
useFrakConfig,
|
|
8
|
+
useGetMerchantInformation,
|
|
9
|
+
useOpenSso,
|
|
10
|
+
usePrepareSso,
|
|
11
|
+
useReferralInteraction,
|
|
12
|
+
useSendTransactionAction,
|
|
13
|
+
useSiweAuthenticate,
|
|
14
|
+
useWalletStatus,
|
|
15
|
+
} from "./hook";
|
|
16
|
+
export type {
|
|
17
|
+
FrakConfigProviderProps,
|
|
18
|
+
FrakIFrameClientProps,
|
|
19
|
+
} from "./provider";
|
|
20
|
+
export {
|
|
21
|
+
FrakConfigContext,
|
|
22
|
+
FrakConfigProvider,
|
|
23
|
+
FrakIFrameClientContext,
|
|
24
|
+
FrakIFrameClientProvider,
|
|
25
|
+
} from "./provider";
|