thirdweb 5.80.0 → 5.80.1-nightly-ce3e850fdbf34911e20919ecc2674e4a63f08fa3-20241225000333
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/dist/cjs/adapters/ethers6.js +1 -1
- package/dist/cjs/contract/deployment/utils/bootstrap.js +29 -11
- package/dist/cjs/contract/deployment/utils/bootstrap.js.map +1 -1
- package/dist/cjs/contract/deployment/zksync/implementations.js +27 -0
- package/dist/cjs/contract/deployment/zksync/implementations.js.map +1 -0
- package/dist/cjs/extensions/prebuilts/get-required-transactions.js +5 -2
- package/dist/cjs/extensions/prebuilts/get-required-transactions.js.map +1 -1
- package/dist/cjs/react/core/utils/walletIcon.js +3 -0
- package/dist/cjs/react/core/utils/walletIcon.js.map +1 -1
- package/dist/cjs/react/web/ui/MediaRenderer/MediaRenderer.js +18 -8
- package/dist/cjs/react/web/ui/MediaRenderer/MediaRenderer.js.map +1 -1
- package/dist/cjs/react/web/ui/MediaRenderer/useResolvedMediaType.js +25 -0
- package/dist/cjs/react/web/ui/MediaRenderer/useResolvedMediaType.js.map +1 -1
- package/dist/cjs/react/web/ui/prebuilt/NFT/media.js +24 -12
- package/dist/cjs/react/web/ui/prebuilt/NFT/media.js.map +1 -1
- package/dist/cjs/react/web/ui/prebuilt/NFT/name.js +24 -12
- package/dist/cjs/react/web/ui/prebuilt/NFT/name.js.map +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/version.js.map +1 -1
- package/dist/cjs/wallets/manager/index.js +2 -2
- package/dist/cjs/wallets/manager/index.js.map +1 -1
- package/dist/esm/adapters/ethers6.js +1 -1
- package/dist/esm/contract/deployment/utils/bootstrap.js +29 -11
- package/dist/esm/contract/deployment/utils/bootstrap.js.map +1 -1
- package/dist/esm/contract/deployment/zksync/implementations.js +24 -0
- package/dist/esm/contract/deployment/zksync/implementations.js.map +1 -0
- package/dist/esm/extensions/prebuilts/get-required-transactions.js +5 -2
- package/dist/esm/extensions/prebuilts/get-required-transactions.js.map +1 -1
- package/dist/esm/react/core/utils/walletIcon.js +3 -0
- package/dist/esm/react/core/utils/walletIcon.js.map +1 -1
- package/dist/esm/react/web/ui/MediaRenderer/MediaRenderer.js +12 -3
- package/dist/esm/react/web/ui/MediaRenderer/MediaRenderer.js.map +1 -1
- package/dist/esm/react/web/ui/MediaRenderer/useResolvedMediaType.js +24 -0
- package/dist/esm/react/web/ui/MediaRenderer/useResolvedMediaType.js.map +1 -1
- package/dist/esm/react/web/ui/prebuilt/NFT/media.js +23 -12
- package/dist/esm/react/web/ui/prebuilt/NFT/media.js.map +1 -1
- package/dist/esm/react/web/ui/prebuilt/NFT/name.js +23 -12
- package/dist/esm/react/web/ui/prebuilt/NFT/name.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/version.js.map +1 -1
- package/dist/esm/wallets/manager/index.js +2 -2
- package/dist/esm/wallets/manager/index.js.map +1 -1
- package/dist/types/adapters/ethers6.d.ts +1 -1
- package/dist/types/contract/deployment/utils/bootstrap.d.ts.map +1 -1
- package/dist/types/contract/deployment/zksync/implementations.d.ts +3 -0
- package/dist/types/contract/deployment/zksync/implementations.d.ts.map +1 -0
- package/dist/types/extensions/prebuilts/get-required-transactions.d.ts +1 -1
- package/dist/types/extensions/prebuilts/get-required-transactions.d.ts.map +1 -1
- package/dist/types/react/core/utils/walletIcon.d.ts +3 -0
- package/dist/types/react/core/utils/walletIcon.d.ts.map +1 -1
- package/dist/types/react/web/ui/MediaRenderer/MediaRenderer.d.ts +12 -0
- package/dist/types/react/web/ui/MediaRenderer/MediaRenderer.d.ts.map +1 -1
- package/dist/types/react/web/ui/MediaRenderer/useResolvedMediaType.d.ts +8 -0
- package/dist/types/react/web/ui/MediaRenderer/useResolvedMediaType.d.ts.map +1 -1
- package/dist/types/react/web/ui/prebuilt/NFT/media.d.ts +10 -0
- package/dist/types/react/web/ui/prebuilt/NFT/media.d.ts.map +1 -1
- package/dist/types/react/web/ui/prebuilt/NFT/name.d.ts +10 -0
- package/dist/types/react/web/ui/prebuilt/NFT/name.d.ts.map +1 -1
- package/dist/types/version.d.ts +1 -1
- package/dist/types/version.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/adapters/ethers6.ts +1 -1
- package/src/contract/deployment/utils/bootstrap.test.ts +44 -1
- package/src/contract/deployment/utils/bootstrap.ts +38 -11
- package/src/contract/deployment/zksync/implementations.ts +24 -0
- package/src/extensions/prebuilts/get-required-transactions.test.ts +14 -1
- package/src/extensions/prebuilts/get-required-transactions.ts +6 -2
- package/src/react/core/hooks/wallets/useSwitchActiveWalletChain.test.tsx +51 -0
- package/src/react/core/providers/connection-manager.test.tsx +11 -0
- package/src/react/core/utils/isSmartWallet.test.ts +19 -0
- package/src/react/core/utils/storage.test.ts +57 -0
- package/src/react/core/utils/structuralSharing.test.ts +55 -0
- package/src/react/core/utils/walletIcon.test.ts +81 -0
- package/src/react/core/utils/walletIcon.ts +3 -0
- package/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx +193 -2
- package/src/react/web/ui/MediaRenderer/MediaRenderer.tsx +12 -3
- package/src/react/web/ui/MediaRenderer/icons.test.tsx +80 -0
- package/src/react/web/ui/MediaRenderer/mime/mime.test.ts +66 -0
- package/src/react/web/ui/MediaRenderer/useResolvedMediaType.test.tsx +27 -0
- package/src/react/web/ui/MediaRenderer/useResolvedMediaType.ts +28 -0
- package/src/react/web/ui/hooks/useCopyClipboard.test.tsx +41 -0
- package/src/react/web/ui/hooks/useDebouncedValue.test.tsx +107 -0
- package/src/react/web/ui/hooks/useShowMore.test.tsx +83 -0
- package/src/react/web/ui/prebuilt/Chain/name.test.tsx +36 -28
- package/src/react/web/ui/prebuilt/NFT/media.test.tsx +58 -1
- package/src/react/web/ui/prebuilt/NFT/media.tsx +32 -13
- package/src/react/web/ui/prebuilt/NFT/name.test.tsx +37 -1
- package/src/react/web/ui/prebuilt/NFT/name.tsx +29 -13
- package/src/react/web/utils/resolveMimeType.test.ts +63 -0
- package/src/version.ts +1 -1
- package/src/wallets/manager/index.ts +2 -2
- package/src/wallets/smart/smart.test.ts +1 -1
@@ -38,6 +38,9 @@ export const genericWalletIcon =
|
|
38
38
|
const passkeyIcon =
|
39
39
|
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzU2MzlfMzIpIj4KPHBhdGggZD0iTTcuOTk5NTkgNi42NjY1QzcuNjQ1OTYgNi42NjY1IDcuMzA2ODMgNi44MDY5OCA3LjA1Njc4IDcuMDU3MDNDNi44MDY3MyA3LjMwNzA4IDYuNjY2MjUgNy42NDYyMiA2LjY2NjI1IDcuOTk5ODRDNi42NjYyNSA4LjY3OTg0IDYuNTk5NTkgOS42NzMxNyA2LjQ5MjkyIDEwLjY2NjUiIHN0cm9rZT0iIzMzODVGRiIgc3Ryb2tlLXdpZHRoPSIxLjI1NDkiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBkPSJNOS4zMzI5MyA4Ljc0NjU4QzkuMzMyOTMgMTAuMzMzMiA5LjMzMjkzIDEyLjk5OTkgOC42NjYyNiAxNC42NjY2IiBzdHJva2U9IiMzMzg1RkYiIHN0cm9rZS13aWR0aD0iMS4yNTQ5IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZD0iTTExLjUyNzMgMTQuMDEzM0MxMS42MDczIDEzLjYxMzMgMTEuODE0IDEyLjQ4IDExLjg2MDcgMTIiIHN0cm9rZT0iIzMzODVGRiIgc3Ryb2tlLXdpZHRoPSIxLjI1NDkiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBkPSJNMS4zMzM5OCA4LjAwMDE2QzEuMzMzOTggNi42MDA5NSAxLjc3NDIzIDUuMjM3MiAyLjU5MjM3IDQuMTAyMDlDMy40MTA1MSAyLjk2Njk5IDQuNTY1MDUgMi4xMTgwOCA1Ljg5MjQ3IDEuNjc1NjFDNy4yMTk4OCAxLjIzMzE0IDguNjUyODYgMS4yMTk1NCA5Ljk4ODQ0IDEuNjM2NzRDMTEuMzI0IDIuMDUzOTQgMTIuNDk0NSAyLjg4MDc5IDEzLjMzNCA0LjAwMDE2IiBzdHJva2U9IiMzMzg1RkYiIHN0cm9rZS13aWR0aD0iMS4yNTQ5IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZD0iTTEuMzMzOTggMTAuNjY2NUgxLjMzOTE0IiBzdHJva2U9IiMzMzg1RkYiIHN0cm9rZS13aWR0aD0iMS4yNTQ5IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZD0iTTE0LjUzMjcgMTAuNjY2NUMxNC42NjYgOS4zMzMxNyAxNC42MiA3LjA5NzE3IDE0LjUzMjcgNi42NjY1IiBzdHJva2U9IiMzMzg1RkYiIHN0cm9rZS13aWR0aD0iMS4yNTQ5IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZD0iTTMuMzMzOTggMTIuOTk5OEMzLjY2NzMyIDExLjk5OTggNC4wMDA2NSA5Ljk5OTg0IDQuMDAwNjUgNy45OTk4NEMzLjk5OTk4IDcuNTQ1NzUgNC4wNzY2MyA3LjA5NDg2IDQuMjI3MzIgNi42NjY1IiBzdHJva2U9IiMzMzg1RkYiIHN0cm9rZS13aWR0aD0iMS4yNTQ5IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZD0iTTUuNzY3MDkgMTQuNjY2OEM1LjkwNzA5IDE0LjIyNjggNi4wNjcwOSAxMy43ODY4IDYuMTQ3MDkgMTMuMzMzNSIgc3Ryb2tlPSIjMzM4NUZGIiBzdHJva2Utd2lkdGg9IjEuMjU0OSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxwYXRoIGQ9Ik02IDQuNTMzNDZDNi42MDgyNyA0LjE4MjI4IDcuMjk4MjggMy45OTc0NSA4LjAwMDY0IDMuOTk3NTZDOC43MDMwMSAzLjk5NzY3IDkuMzkyOTYgNC4xODI3MiAxMC4wMDExIDQuNTM0MUMxMC42MDkzIDQuODg1NDggMTEuMTE0MiA1LjM5MDc5IDExLjQ2NTEgNS45OTkyM0MxMS44MTYgNi42MDc2NiAxMi4wMDA1IDcuMjk3NzYgMTIgOC4wMDAxMlY5LjMzMzQ2IiBzdHJva2U9IiMzMzg1RkYiIHN0cm9rZS13aWR0aD0iMS4yNTQ5IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMF81NjM5XzMyIj4KPHJlY3Qgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJ3aGl0ZSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo=";
|
40
40
|
|
41
|
+
/**
|
42
|
+
* @internal
|
43
|
+
*/
|
41
44
|
export const socialIcons = {
|
42
45
|
google: googleIconUri,
|
43
46
|
apple: appleIconUri,
|
@@ -1,11 +1,18 @@
|
|
1
|
-
import
|
1
|
+
import React from "react";
|
2
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
2
3
|
import {
|
4
|
+
fireEvent,
|
3
5
|
render,
|
4
6
|
screen,
|
5
7
|
waitFor,
|
6
8
|
} from "../../../../../test/src/react-render.js";
|
7
9
|
import { TEST_CLIENT } from "../../../../../test/src/test-clients.js";
|
8
|
-
import {
|
10
|
+
import {
|
11
|
+
IframePlayer,
|
12
|
+
LinkPlayer,
|
13
|
+
MediaRenderer,
|
14
|
+
mergeRefs,
|
15
|
+
} from "./MediaRenderer.js";
|
9
16
|
|
10
17
|
describe("MediaRenderer", () => {
|
11
18
|
it("should render nothing if no src provided", () => {
|
@@ -31,4 +38,188 @@ describe("MediaRenderer", () => {
|
|
31
38
|
expect(screen.getByRole("img")).toBeInTheDocument();
|
32
39
|
});
|
33
40
|
});
|
41
|
+
|
42
|
+
describe("mergeRefs", () => {
|
43
|
+
it("should call all callback refs with the given value", () => {
|
44
|
+
const callbackRef1 = vi.fn();
|
45
|
+
const callbackRef2 = vi.fn();
|
46
|
+
|
47
|
+
const mergedRef = mergeRefs([callbackRef1, callbackRef2]);
|
48
|
+
|
49
|
+
const testValue = { test: "value" };
|
50
|
+
mergedRef(testValue);
|
51
|
+
|
52
|
+
expect(callbackRef1).toHaveBeenCalledWith(testValue);
|
53
|
+
expect(callbackRef2).toHaveBeenCalledWith(testValue);
|
54
|
+
});
|
55
|
+
|
56
|
+
it("should assign the value to all mutable object refs", () => {
|
57
|
+
const mutableRef1 = React.createRef();
|
58
|
+
const mutableRef2 = React.createRef();
|
59
|
+
|
60
|
+
const mergedRef = mergeRefs([mutableRef1, mutableRef2]);
|
61
|
+
|
62
|
+
const testValue = { test: "value" };
|
63
|
+
mergedRef(testValue);
|
64
|
+
|
65
|
+
expect(mutableRef1.current).toBe(testValue);
|
66
|
+
expect(mutableRef2.current).toBe(testValue);
|
67
|
+
});
|
68
|
+
|
69
|
+
it("should handle a mix of callback and mutable object refs", () => {
|
70
|
+
const callbackRef = vi.fn();
|
71
|
+
const mutableRef = React.createRef();
|
72
|
+
|
73
|
+
const mergedRef = mergeRefs([callbackRef, mutableRef]);
|
74
|
+
|
75
|
+
const testValue = { test: "value" };
|
76
|
+
mergedRef(testValue);
|
77
|
+
|
78
|
+
expect(callbackRef).toHaveBeenCalledWith(testValue);
|
79
|
+
expect(mutableRef.current).toBe(testValue);
|
80
|
+
});
|
81
|
+
|
82
|
+
it("should do nothing if refs array is empty", () => {
|
83
|
+
const mergedRef = mergeRefs([]);
|
84
|
+
|
85
|
+
expect(() => mergedRef(null)).not.toThrow();
|
86
|
+
});
|
87
|
+
});
|
88
|
+
|
89
|
+
describe("LinkPlayer", () => {
|
90
|
+
it("renders with default props", () => {
|
91
|
+
render(<LinkPlayer />);
|
92
|
+
|
93
|
+
const linkElement = screen.getByText("File");
|
94
|
+
expect(linkElement).toBeInTheDocument();
|
95
|
+
expect(linkElement.tagName).toBe("A");
|
96
|
+
expect(linkElement).toHaveAttribute("target", "_blank");
|
97
|
+
expect(linkElement).toHaveAttribute("rel", "noopener noreferrer");
|
98
|
+
});
|
99
|
+
|
100
|
+
it("renders with custom src and alt", () => {
|
101
|
+
const src = "https://example.com/file";
|
102
|
+
const alt = "Custom File Name";
|
103
|
+
render(<LinkPlayer src={src} alt={alt} />);
|
104
|
+
|
105
|
+
const linkElement = screen.getByText(alt);
|
106
|
+
expect(linkElement).toBeInTheDocument();
|
107
|
+
expect(linkElement).toHaveAttribute("href", src);
|
108
|
+
});
|
109
|
+
|
110
|
+
it("applies custom style and className", () => {
|
111
|
+
const customStyle = { backgroundColor: "red" };
|
112
|
+
const customClassName = "custom-class";
|
113
|
+
const { container } = render(
|
114
|
+
<LinkPlayer style={customStyle} className={customClassName} />,
|
115
|
+
);
|
116
|
+
|
117
|
+
const outerDiv = container.querySelector(".custom-class");
|
118
|
+
if (!outerDiv) {
|
119
|
+
throw new Error("Failed to render LinkPlayer");
|
120
|
+
}
|
121
|
+
const styles = window.getComputedStyle(outerDiv);
|
122
|
+
expect(styles.backgroundColor).toBe("red");
|
123
|
+
});
|
124
|
+
|
125
|
+
it("forwards ref to anchor element", () => {
|
126
|
+
const ref = React.createRef<HTMLAnchorElement>();
|
127
|
+
render(<LinkPlayer ref={ref} />);
|
128
|
+
|
129
|
+
expect(ref.current).toBeInstanceOf(HTMLAnchorElement);
|
130
|
+
});
|
131
|
+
});
|
132
|
+
|
133
|
+
describe("IframePlayer", () => {
|
134
|
+
beforeEach(() => {
|
135
|
+
// Reset the body before each test
|
136
|
+
document.body.innerHTML = "";
|
137
|
+
});
|
138
|
+
|
139
|
+
it("renders with default props", () => {
|
140
|
+
render(<IframePlayer />);
|
141
|
+
|
142
|
+
const iframe = screen.getByTitle("thirdweb iframe player");
|
143
|
+
expect(iframe).toBeInTheDocument();
|
144
|
+
expect(iframe.tagName).toBe("IFRAME");
|
145
|
+
expect(iframe).toHaveAttribute("sandbox", "allow-scripts");
|
146
|
+
expect(iframe).toHaveAttribute(
|
147
|
+
"allow",
|
148
|
+
"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture",
|
149
|
+
);
|
150
|
+
});
|
151
|
+
|
152
|
+
it("applies custom src and alt", () => {
|
153
|
+
const src = "https://example.com/video";
|
154
|
+
const alt = "Custom Video";
|
155
|
+
render(<IframePlayer src={src} alt={alt} />);
|
156
|
+
|
157
|
+
const iframe = screen.getByTitle(alt);
|
158
|
+
expect(iframe).toBeInTheDocument();
|
159
|
+
expect(iframe).toHaveAttribute("src", src);
|
160
|
+
});
|
161
|
+
|
162
|
+
it("renders poster image when provided", () => {
|
163
|
+
const poster = "https://example.com/poster.jpg";
|
164
|
+
render(<IframePlayer poster={poster} />);
|
165
|
+
|
166
|
+
const posterImg = screen.getByRole("img");
|
167
|
+
expect(posterImg).toBeInTheDocument();
|
168
|
+
expect(posterImg).toHaveAttribute("src", poster);
|
169
|
+
});
|
170
|
+
|
171
|
+
it("toggles play state when PlayButton is clicked", async () => {
|
172
|
+
render(<IframePlayer src="https://example.com/video" />);
|
173
|
+
|
174
|
+
const playButton = screen.getByRole("button");
|
175
|
+
expect(playButton).toBeInTheDocument();
|
176
|
+
|
177
|
+
const iframe = screen.getByTitle("thirdweb iframe player");
|
178
|
+
expect(iframe).toHaveAttribute("src", "https://example.com/video");
|
179
|
+
|
180
|
+
fireEvent.click(playButton);
|
181
|
+
|
182
|
+
// After clicking, the iframe src should be undefined
|
183
|
+
expect(iframe).not.toHaveAttribute("src");
|
184
|
+
|
185
|
+
fireEvent.click(playButton);
|
186
|
+
|
187
|
+
// After clicking again, the iframe src should be restored
|
188
|
+
expect(iframe).toHaveAttribute("src", "https://example.com/video");
|
189
|
+
});
|
190
|
+
|
191
|
+
it("applies custom style", () => {
|
192
|
+
const customStyle = { backgroundColor: "red" };
|
193
|
+
const { container } = render(
|
194
|
+
<IframePlayer style={customStyle} className="iframe-test" />,
|
195
|
+
);
|
196
|
+
|
197
|
+
const element = container.querySelector(".iframe-test");
|
198
|
+
if (!element) {
|
199
|
+
throw new Error("no iframe detected");
|
200
|
+
}
|
201
|
+
expect(element).toHaveStyle("background-color: red");
|
202
|
+
});
|
203
|
+
|
204
|
+
it("forwards ref to iframe element", () => {
|
205
|
+
const ref = React.createRef<HTMLIFrameElement>();
|
206
|
+
render(<IframePlayer ref={ref} />);
|
207
|
+
|
208
|
+
expect(ref.current).toBeInstanceOf(HTMLIFrameElement);
|
209
|
+
});
|
210
|
+
|
211
|
+
it("respects requireInteraction prop", () => {
|
212
|
+
render(
|
213
|
+
<IframePlayer src="https://example.com/video" requireInteraction />,
|
214
|
+
);
|
215
|
+
|
216
|
+
const iframe = screen.getByTitle("thirdweb iframe player");
|
217
|
+
expect(iframe).not.toHaveAttribute("src");
|
218
|
+
|
219
|
+
const playButton = screen.getByRole("button");
|
220
|
+
fireEvent.click(playButton);
|
221
|
+
|
222
|
+
expect(iframe).toHaveAttribute("src", "https://example.com/video");
|
223
|
+
});
|
224
|
+
});
|
34
225
|
});
|
@@ -521,7 +521,10 @@ const AudioPlayer = /* @__PURE__ */ (() =>
|
|
521
521
|
);
|
522
522
|
}))();
|
523
523
|
|
524
|
-
|
524
|
+
/**
|
525
|
+
* @internal Exported for tests
|
526
|
+
*/
|
527
|
+
export const IframePlayer = /* @__PURE__ */ (() =>
|
525
528
|
React.forwardRef<
|
526
529
|
HTMLIFrameElement,
|
527
530
|
Omit<
|
@@ -588,7 +591,10 @@ const IframePlayer = /* @__PURE__ */ (() =>
|
|
588
591
|
);
|
589
592
|
}))();
|
590
593
|
|
591
|
-
|
594
|
+
/**
|
595
|
+
* @internal Exported for tests
|
596
|
+
*/
|
597
|
+
export const LinkPlayer = /* @__PURE__ */ (() =>
|
592
598
|
React.forwardRef<
|
593
599
|
HTMLAnchorElement,
|
594
600
|
Pick<MediaRendererProps, "src" | "alt" | "style" | "className">
|
@@ -640,8 +646,11 @@ const LinkPlayer = /* @__PURE__ */ (() =>
|
|
640
646
|
);
|
641
647
|
}))();
|
642
648
|
|
649
|
+
/**
|
650
|
+
* @internal
|
651
|
+
*/
|
643
652
|
// biome-ignore lint/suspicious/noExplicitAny: TODO: fix any
|
644
|
-
function mergeRefs<T = any>(
|
653
|
+
export function mergeRefs<T = any>(
|
645
654
|
refs: Array<React.MutableRefObject<T> | React.LegacyRef<T>>,
|
646
655
|
): React.RefCallback<T> {
|
647
656
|
return (value) => {
|
@@ -0,0 +1,80 @@
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
2
|
+
import { render } from "~test/react-render.js";
|
3
|
+
import {
|
4
|
+
CarbonDocumentAudio,
|
5
|
+
CarbonDocumentUnknown,
|
6
|
+
CarbonPauseFilled,
|
7
|
+
CarbonPlayFilledAlt,
|
8
|
+
} from "./icons.js";
|
9
|
+
|
10
|
+
describe("MediaRenderer Icons", () => {
|
11
|
+
it("renders CarbonDocumentUnknown correctly", () => {
|
12
|
+
const { container } = render(<CarbonDocumentUnknown />);
|
13
|
+
expect(container.querySelector("svg")).toBeInTheDocument();
|
14
|
+
expect(container.querySelector("svg")).toHaveAttribute("width", "16");
|
15
|
+
expect(container.querySelector("svg")).toHaveAttribute("height", "16");
|
16
|
+
expect(container.querySelector("svg")).toHaveAttribute(
|
17
|
+
"viewBox",
|
18
|
+
"0 0 32 32",
|
19
|
+
);
|
20
|
+
expect(container.querySelector("svg")).toHaveAttribute(
|
21
|
+
"role",
|
22
|
+
"presentation",
|
23
|
+
);
|
24
|
+
});
|
25
|
+
|
26
|
+
it("renders CarbonDocumentAudio correctly", () => {
|
27
|
+
const { container } = render(<CarbonDocumentAudio />);
|
28
|
+
expect(container.querySelector("svg")).toBeInTheDocument();
|
29
|
+
expect(container.querySelector("svg")).toHaveAttribute("width", "16");
|
30
|
+
expect(container.querySelector("svg")).toHaveAttribute("height", "16");
|
31
|
+
expect(container.querySelector("svg")).toHaveAttribute(
|
32
|
+
"viewBox",
|
33
|
+
"0 0 32 32",
|
34
|
+
);
|
35
|
+
expect(container.querySelector("svg")).toHaveAttribute(
|
36
|
+
"role",
|
37
|
+
"presentation",
|
38
|
+
);
|
39
|
+
});
|
40
|
+
|
41
|
+
it("renders CarbonPauseFilled correctly", () => {
|
42
|
+
const { container } = render(<CarbonPauseFilled />);
|
43
|
+
expect(container.querySelector("svg")).toBeInTheDocument();
|
44
|
+
expect(container.querySelector("svg")).toHaveAttribute("width", "16");
|
45
|
+
expect(container.querySelector("svg")).toHaveAttribute("height", "16");
|
46
|
+
expect(container.querySelector("svg")).toHaveAttribute(
|
47
|
+
"viewBox",
|
48
|
+
"0 0 32 32",
|
49
|
+
);
|
50
|
+
expect(container.querySelector("svg")).toHaveAttribute(
|
51
|
+
"role",
|
52
|
+
"presentation",
|
53
|
+
);
|
54
|
+
});
|
55
|
+
|
56
|
+
it("renders CarbonPlayFilledAlt correctly", () => {
|
57
|
+
const { container } = render(<CarbonPlayFilledAlt />);
|
58
|
+
expect(container.querySelector("svg")).toBeInTheDocument();
|
59
|
+
expect(container.querySelector("svg")).toHaveAttribute("width", "16");
|
60
|
+
expect(container.querySelector("svg")).toHaveAttribute("height", "16");
|
61
|
+
expect(container.querySelector("svg")).toHaveAttribute(
|
62
|
+
"viewBox",
|
63
|
+
"0 0 32 32",
|
64
|
+
);
|
65
|
+
expect(container.querySelector("svg")).toHaveAttribute(
|
66
|
+
"role",
|
67
|
+
"presentation",
|
68
|
+
);
|
69
|
+
});
|
70
|
+
|
71
|
+
it("applies custom props to the svg element", () => {
|
72
|
+
const { container } = render(
|
73
|
+
<CarbonDocumentUnknown data-testid="custom-icon" />,
|
74
|
+
);
|
75
|
+
expect(container.querySelector("svg")).toHaveAttribute(
|
76
|
+
"data-testid",
|
77
|
+
"custom-icon",
|
78
|
+
);
|
79
|
+
});
|
80
|
+
});
|
@@ -0,0 +1,66 @@
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
2
|
+
import { getMimeTypeFromUrl } from "./mime.js";
|
3
|
+
import { extensionsToMimeType } from "./types.js";
|
4
|
+
|
5
|
+
describe("getMimeTypeFromUrl", () => {
|
6
|
+
it("should return correct mime type for valid file extensions", () => {
|
7
|
+
expect(getMimeTypeFromUrl("https://example.com/image.jpg")).toBe(
|
8
|
+
"image/jpeg",
|
9
|
+
);
|
10
|
+
expect(getMimeTypeFromUrl("https://example.com/audio.mp3")).toBe(
|
11
|
+
"audio/mpeg",
|
12
|
+
);
|
13
|
+
expect(getMimeTypeFromUrl("https://example.com/video.mp4")).toBe(
|
14
|
+
"video/mp4",
|
15
|
+
);
|
16
|
+
expect(getMimeTypeFromUrl("https://example.com/document.html")).toBe(
|
17
|
+
"text/html",
|
18
|
+
);
|
19
|
+
expect(getMimeTypeFromUrl("https://example.com/model.gltf")).toBe(
|
20
|
+
"model/gltf+json",
|
21
|
+
);
|
22
|
+
});
|
23
|
+
|
24
|
+
it("should be case-insensitive for file extensions", () => {
|
25
|
+
expect(getMimeTypeFromUrl("https://example.com/IMAGE.JPG")).toBe(
|
26
|
+
"image/jpeg",
|
27
|
+
);
|
28
|
+
expect(getMimeTypeFromUrl("https://example.com/AUDIO.MP3")).toBe(
|
29
|
+
"audio/mpeg",
|
30
|
+
);
|
31
|
+
});
|
32
|
+
|
33
|
+
it("should return null for unknown file extensions", () => {
|
34
|
+
expect(getMimeTypeFromUrl("https://example.com/unknown.xyz")).toBeNull();
|
35
|
+
});
|
36
|
+
|
37
|
+
it("should handle URLs without file extensions", () => {
|
38
|
+
expect(getMimeTypeFromUrl("https://example.com/noextension")).toBeNull();
|
39
|
+
expect(getMimeTypeFromUrl("https://example.com/")).toBeNull();
|
40
|
+
});
|
41
|
+
|
42
|
+
it("should handle URLs with only a file name", () => {
|
43
|
+
expect(getMimeTypeFromUrl("image.jpg")).toBe("image/jpeg");
|
44
|
+
expect(getMimeTypeFromUrl("audio.mp3")).toBe("audio/mpeg");
|
45
|
+
});
|
46
|
+
|
47
|
+
it("should handle URLs with special characters", () => {
|
48
|
+
expect(
|
49
|
+
getMimeTypeFromUrl("https://example.com/image%20with%20spaces.png"),
|
50
|
+
).toBe("image/png");
|
51
|
+
expect(
|
52
|
+
getMimeTypeFromUrl(
|
53
|
+
"https://example.com/file-name_with-special_chars.mp4",
|
54
|
+
),
|
55
|
+
).toBe("video/mp4");
|
56
|
+
});
|
57
|
+
|
58
|
+
it("should return correct mime type for all defined extensions", () => {
|
59
|
+
// biome-ignore lint/complexity/noForEach: Relax just a test
|
60
|
+
Object.entries(extensionsToMimeType).forEach(([extension, mimeType]) => {
|
61
|
+
expect(getMimeTypeFromUrl(`https://example.com/file.${extension}`)).toBe(
|
62
|
+
mimeType,
|
63
|
+
);
|
64
|
+
});
|
65
|
+
});
|
66
|
+
});
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
2
|
+
import { TEST_CLIENT } from "~test/test-clients.js";
|
3
|
+
import { resolveMediaTypeFromUri } from "./useResolvedMediaType.js";
|
4
|
+
|
5
|
+
const client = TEST_CLIENT;
|
6
|
+
|
7
|
+
describe("useResolvedMediaType", () => {
|
8
|
+
it("resolveMediaTypeFromUri should return an empty string if uri is falsy", () => {
|
9
|
+
expect(resolveMediaTypeFromUri({ client })).toBe("");
|
10
|
+
});
|
11
|
+
|
12
|
+
it("resolveMediaTypeFromUri should resolve Arweave scheme", () => {
|
13
|
+
expect(resolveMediaTypeFromUri({ client, uri: "ar://test" })).toBe(
|
14
|
+
"https://arweave.net/test",
|
15
|
+
);
|
16
|
+
});
|
17
|
+
|
18
|
+
it("resolveMediaTypeFromUri should resolve IPFS uri with custom gateway", () => {
|
19
|
+
expect(
|
20
|
+
resolveMediaTypeFromUri({
|
21
|
+
client,
|
22
|
+
uri: "ipfs://test",
|
23
|
+
gatewayUrl: "https://cf-ipfs.com/ipfs/",
|
24
|
+
}),
|
25
|
+
).toBe("https://cf-ipfs.com/ipfs/test");
|
26
|
+
});
|
27
|
+
});
|
@@ -46,3 +46,31 @@ export function useResolvedMediaType(
|
|
46
46
|
isFetched: resolvedMimeType.isFetched || !!mimeType,
|
47
47
|
};
|
48
48
|
}
|
49
|
+
|
50
|
+
/**
|
51
|
+
* @internal Exported for tests
|
52
|
+
*/
|
53
|
+
export function resolveMediaTypeFromUri(props: {
|
54
|
+
uri?: string;
|
55
|
+
client: ThirdwebClient;
|
56
|
+
gatewayUrl?: string;
|
57
|
+
}) {
|
58
|
+
const { uri, client, gatewayUrl } = props;
|
59
|
+
if (!uri) {
|
60
|
+
return "";
|
61
|
+
}
|
62
|
+
if (uri.startsWith("ar://")) {
|
63
|
+
return resolveArweaveScheme({ uri, gatewayUrl });
|
64
|
+
}
|
65
|
+
if (gatewayUrl) {
|
66
|
+
return uri.replace("ipfs://", gatewayUrl);
|
67
|
+
}
|
68
|
+
try {
|
69
|
+
return resolveScheme({
|
70
|
+
client,
|
71
|
+
uri,
|
72
|
+
});
|
73
|
+
} catch {
|
74
|
+
return uri.replace("ipfs://", "https://ipfs.io/ipfs/");
|
75
|
+
}
|
76
|
+
}
|
@@ -0,0 +1,41 @@
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
2
|
+
import { act, renderHook } from "~test/react-render.js";
|
3
|
+
import { useClipboard } from "./useCopyClipboard.js";
|
4
|
+
|
5
|
+
describe("useClipboard", () => {
|
6
|
+
beforeEach(() => {
|
7
|
+
vi.useFakeTimers();
|
8
|
+
});
|
9
|
+
|
10
|
+
afterEach(() => {
|
11
|
+
vi.useRealTimers();
|
12
|
+
});
|
13
|
+
|
14
|
+
it("should copy text to clipboard and set hasCopied to true", async () => {
|
15
|
+
const { result } = renderHook(() => useClipboard("Hello World"));
|
16
|
+
|
17
|
+
// Simulate copying text
|
18
|
+
await act(async () => {
|
19
|
+
await result.current.onCopy();
|
20
|
+
});
|
21
|
+
|
22
|
+
// Check if hasCopied is true after copying
|
23
|
+
expect(result.current.hasCopied).toBe(true);
|
24
|
+
});
|
25
|
+
|
26
|
+
it("should reset hasCopied to false after 1.5 seconds", async () => {
|
27
|
+
const { result } = renderHook(() => useClipboard("Hello World"));
|
28
|
+
|
29
|
+
await act(async () => {
|
30
|
+
await result.current.onCopy();
|
31
|
+
});
|
32
|
+
|
33
|
+
expect(result.current.hasCopied).toBe(true);
|
34
|
+
|
35
|
+
act(() => {
|
36
|
+
vi.advanceTimersByTime(1500);
|
37
|
+
});
|
38
|
+
|
39
|
+
expect(result.current.hasCopied).toBe(false);
|
40
|
+
});
|
41
|
+
});
|
@@ -0,0 +1,107 @@
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
2
|
+
import { act, renderHook } from "~test/react-render.js";
|
3
|
+
import { useDebouncedValue } from "./useDebouncedValue.js";
|
4
|
+
|
5
|
+
describe("useDebouncedValue", () => {
|
6
|
+
beforeEach(() => {
|
7
|
+
vi.useFakeTimers();
|
8
|
+
});
|
9
|
+
|
10
|
+
afterEach(() => {
|
11
|
+
vi.useRealTimers();
|
12
|
+
});
|
13
|
+
|
14
|
+
it("should return the initial value immediately", () => {
|
15
|
+
const { result } = renderHook(() => useDebouncedValue("initial", 1000));
|
16
|
+
expect(result.current).toBe("initial");
|
17
|
+
});
|
18
|
+
|
19
|
+
it("should debounce value changes", () => {
|
20
|
+
const { result, rerender } = renderHook(
|
21
|
+
({ value, delay }) => useDebouncedValue(value, delay),
|
22
|
+
{ initialProps: { value: "initial", delay: 1000 } },
|
23
|
+
);
|
24
|
+
|
25
|
+
expect(result.current).toBe("initial");
|
26
|
+
|
27
|
+
// Change the value
|
28
|
+
rerender({ value: "changed", delay: 1000 });
|
29
|
+
|
30
|
+
// The value should not change immediately
|
31
|
+
expect(result.current).toBe("initial");
|
32
|
+
|
33
|
+
// Fast-forward time by 500ms
|
34
|
+
act(() => {
|
35
|
+
vi.advanceTimersByTime(500);
|
36
|
+
});
|
37
|
+
|
38
|
+
// The value should still not have changed
|
39
|
+
expect(result.current).toBe("initial");
|
40
|
+
|
41
|
+
// Fast-forward time to just before the delay
|
42
|
+
act(() => {
|
43
|
+
vi.advanceTimersByTime(499);
|
44
|
+
});
|
45
|
+
|
46
|
+
// The value should still not have changed
|
47
|
+
expect(result.current).toBe("initial");
|
48
|
+
|
49
|
+
// Fast-forward time to the full delay
|
50
|
+
act(() => {
|
51
|
+
vi.advanceTimersByTime(1);
|
52
|
+
});
|
53
|
+
|
54
|
+
// Now the value should have changed
|
55
|
+
expect(result.current).toBe("changed");
|
56
|
+
});
|
57
|
+
|
58
|
+
it("should handle multiple rapid changes", () => {
|
59
|
+
const { result, rerender } = renderHook(
|
60
|
+
({ value, delay }) => useDebouncedValue(value, delay),
|
61
|
+
{ initialProps: { value: "initial", delay: 1000 } },
|
62
|
+
);
|
63
|
+
|
64
|
+
rerender({ value: "change1", delay: 1000 });
|
65
|
+
rerender({ value: "change2", delay: 1000 });
|
66
|
+
rerender({ value: "change3", delay: 1000 });
|
67
|
+
|
68
|
+
expect(result.current).toBe("initial");
|
69
|
+
|
70
|
+
act(() => {
|
71
|
+
vi.advanceTimersByTime(1000);
|
72
|
+
});
|
73
|
+
|
74
|
+
expect(result.current).toBe("change3");
|
75
|
+
});
|
76
|
+
|
77
|
+
it("should respect changing delay times", () => {
|
78
|
+
const { result, rerender } = renderHook(
|
79
|
+
({ value, delay }) => useDebouncedValue(value, delay),
|
80
|
+
{ initialProps: { value: "initial", delay: 1000 } },
|
81
|
+
);
|
82
|
+
|
83
|
+
rerender({ value: "changed", delay: 500 });
|
84
|
+
|
85
|
+
act(() => {
|
86
|
+
vi.advanceTimersByTime(600);
|
87
|
+
});
|
88
|
+
|
89
|
+
expect(result.current).toBe("changed");
|
90
|
+
});
|
91
|
+
|
92
|
+
it("should cancel debounce on unmount", () => {
|
93
|
+
const { result, unmount } = renderHook(() =>
|
94
|
+
useDebouncedValue("initial", 1000),
|
95
|
+
);
|
96
|
+
|
97
|
+
expect(result.current).toBe("initial");
|
98
|
+
|
99
|
+
act(() => {
|
100
|
+
unmount();
|
101
|
+
vi.advanceTimersByTime(1000);
|
102
|
+
});
|
103
|
+
|
104
|
+
// The value should remain 'initial' as the effect should have been cleaned up
|
105
|
+
expect(result.current).toBe("initial");
|
106
|
+
});
|
107
|
+
});
|