Skip to content

Commit 42d5c65

Browse files
committed
[SDK] NFT Prebuilt components (#4179)
## Problem solved Short description of the bug fixed or feature added <!-- start pr-codex --> --- ## PR-Codex overview This PR adds NFT prebuilt components for displaying NFT metadata, including name, description, and media. ### Detailed summary - Added `image_url` property to `NFT` type - Introduced NFT prebuilt components: `NFT`, `NFT.Media`, `NFT.Name`, `NFT.Description` - Implemented functions to fetch and display NFT metadata - Updated test cases for NFT components > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent 81c9ee9 commit 42d5c65

File tree

5 files changed

+410
-0
lines changed

5 files changed

+410
-0
lines changed

.changeset/hip-teachers-drive.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
"thirdweb": minor
3+
---
4+
5+
Add NFT prebuilt components
6+
```tsx
7+
import { getContract } from "thirdweb";
8+
import { NFT } from "thirdweb/react";
9+
10+
const contract = getContract({
11+
address: "0x...",
12+
chain: ethereum,
13+
client: yourThirdwebClient,
14+
});
15+
16+
<NFT contract={contract} tokenId={0n}>
17+
<Suspense fallback={"Loading media..."}>
18+
<NFT.Media />
19+
</Suspense>
20+
</NFT>
21+
```

packages/thirdweb/src/exports/react.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,11 @@ export {
171171
type CreateDirectListingButtonProps,
172172
} from "../react/web/ui/prebuilt/thirdweb/CreateDirectListingButton/index.js";
173173

174+
export {
175+
NFT,
176+
type NFTMediaProps,
177+
} from "../react/web/ui/prebuilt/NFT/NFT.js";
178+
174179
export { useConnectionManager } from "../react/core/providers/connection-manager.js";
175180

176181
/**
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { useContext } from "react";
2+
import { describe, expect, it } from "vitest";
3+
import { render, screen, waitFor } from "~test/react-render.js";
4+
import { DOODLES_CONTRACT } from "~test/test-contracts.js";
5+
import { NFT, NFTProviderContext, getNFTInfo } from "./NFT.js";
6+
7+
describe.runIf(process.env.TW_SECRET_KEY)("NFT prebuilt component", () => {
8+
it("should fetch the NFT metadata", async () => {
9+
const nft = await getNFTInfo({
10+
contract: DOODLES_CONTRACT,
11+
tokenId: 1n,
12+
});
13+
expect(nft.metadata).toStrictEqual({
14+
attributes: [
15+
{
16+
trait_type: "face",
17+
value: "holographic beard",
18+
},
19+
{
20+
trait_type: "hair",
21+
value: "white bucket cap",
22+
},
23+
{
24+
trait_type: "body",
25+
value: "purple sweater with satchel",
26+
},
27+
{
28+
trait_type: "background",
29+
value: "grey",
30+
},
31+
{
32+
trait_type: "head",
33+
value: "gradient 2",
34+
},
35+
],
36+
description:
37+
"A community-driven collectibles project featuring art by Burnt Toast. Doodles come in a joyful range of colors, traits and sizes with a collection size of 10,000. Each Doodle allows its owner to vote for experiences and activations paid for by the Doodles Community Treasury. Burnt Toast is the working alias for Scott Martin, a Canadian–based illustrator, designer, animator and muralist.",
38+
image: "ipfs://QmTDxnzcvj2p3xBrKcGv1wxoyhAn2yzCQnZZ9LmFjReuH9",
39+
name: "Doodle #1",
40+
});
41+
});
42+
43+
it("should render children correctly", () => {
44+
render(
45+
<NFT contract={DOODLES_CONTRACT} tokenId={0n}>
46+
<div>Child Component</div>
47+
</NFT>,
48+
);
49+
50+
expect(screen.getByText("Child Component")).toBeInTheDocument();
51+
});
52+
53+
it("should provide context values to children", () => {
54+
function NFTConsumer() {
55+
const context = useContext(NFTProviderContext);
56+
if (!context) {
57+
return <div>No context</div>;
58+
}
59+
return (
60+
<div>
61+
Contract: {String(context.contract)}, Token ID:{" "}
62+
{context.tokenId.toString()}
63+
</div>
64+
);
65+
}
66+
render(
67+
<NFT contract={DOODLES_CONTRACT} tokenId={0n}>
68+
<NFTConsumer />
69+
</NFT>,
70+
);
71+
72+
expect(screen.getByText(/Contract:/)).toBeInTheDocument();
73+
expect(screen.getByText(/Token ID: 0/)).toBeInTheDocument();
74+
});
75+
76+
it("should render the NFT image", () => {
77+
render(
78+
<NFT contract={DOODLES_CONTRACT} tokenId={0n}>
79+
<NFT.Media />
80+
</NFT>,
81+
);
82+
83+
waitFor(() => expect(screen.getByRole("img")).toBeInTheDocument());
84+
});
85+
86+
it("should render the NFT name", () => {
87+
render(
88+
<NFT contract={DOODLES_CONTRACT} tokenId={1n}>
89+
<NFT.Name />
90+
</NFT>,
91+
);
92+
93+
waitFor(() => expect(screen.getByText("Doodle #1")).toBeInTheDocument());
94+
});
95+
96+
it("should render the NFT description", () => {
97+
render(
98+
<NFT contract={DOODLES_CONTRACT} tokenId={1n}>
99+
<NFT.Name />
100+
</NFT>,
101+
);
102+
103+
waitFor(() =>
104+
expect(
105+
screen.getByText(
106+
"A community-driven collectibles project featuring art by Burnt Toast",
107+
),
108+
).toBeInTheDocument(),
109+
);
110+
});
111+
});

0 commit comments

Comments
 (0)