Skip to content

Commit b838a49

Browse files
Add more components to Snaps Custom UI (#1835)
* Add more components to Snaps Custom UI This PR was made with Cursor AI. I have attempted to the best of my ability to make sure these edits are accurate. * Update whats-new.md * Separate links per component in what's new Co-authored-by: Alexandra Carrillo <[email protected]> * Update Banner description text Co-authored-by: Alexandra Carrillo <[email protected]> * Update props for container Co-authored-by: Alexandra Carrillo <[email protected]> * Update footer description Co-authored-by: Alexandra Carrillo <[email protected]> * Update footer props Co-authored-by: Alexandra Carrillo <[email protected]> * Update Value props Co-authored-by: Alexandra Carrillo <[email protected]> * Move Banner above Bold * Fix props for Banner and add Skeleton component * Add Value as a child of Row * Add props for Address and update children for Text * Update Avatar with new prop * Add prop for Box and update What's New * Apply suggestions from code review * Add defaults to Skeleton props * Cleanup Remove erroneous message about Address tooltip Make checkbox variant prop description match style of document. Make Skeleton defaults descriptions match style of document. --------- Co-authored-by: Alexandra Carrillo <[email protected]>
1 parent 1f282b1 commit b838a49

File tree

2 files changed

+246
-11
lines changed

2 files changed

+246
-11
lines changed

docs/whats-new.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ The latest major MetaMask documentation updates are listed by the month they wer
99
For a comprehensive list of recent product changes, visit the "Release Notes" section at the bottom
1010
of the [MetaMask developer page](https://metamask.io/developer/).
1111

12+
## January 2025
13+
14+
- Documented Snaps [`Banner`](/snaps/features/custom-ui/#banner), [`Container`](/snaps/features/custom-ui/#container), [`Footer`](/snaps/features/custom-ui/#footer), [`Skeleton`](/snaps/features/custom-ui/#skeleton), and [`Value`](/snaps/features/custom-ui/#value) UI components.
15+
([#1835](https://github.com/MetaMask/metamask-docs/pull/1835))
16+
1217
## December 2024
1318

1419
- Documented [Swellchain](/services/reference/swellchain) support. ([#1776](https://github.com/MetaMask/metamask-docs/pull/1776))

snaps/features/custom-ui/index.md

Lines changed: 241 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,16 @@ The following custom UI components are available:
6565

6666
Outputs a formatted text field for a blockchain address.
6767
The address is automatically displayed with a [Jazzicon](https://www.npmjs.com/package/@metamask/jazzicon)
68-
and truncated value.
69-
Hovering over the address shows the full value in a tooltip.
68+
and truncated value.
7069

7170
#### Props
7271

7372
- `address`: `string` - A valid Ethereum address, starting with `0x`, or a valid
7473
[CAIP-10](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-10.md) address.
74+
- `truncate`: `boolean` - (Optional) Whether to truncate the address. The default is `true`.
75+
- `displayName`: `boolean` - (Optional) Whether to display the internally saved account label in place of the address.
76+
The default is `true`.
77+
- `avatar`: `boolean` - (Optional) Whether to show the address Jazzicon. The default is `true`.
7578

7679
#### Example
7780

@@ -133,30 +136,76 @@ await snap.request({
133136
134137
Outputs a [Jazzicon](https://www.npmjs.com/package/@metamask/jazzicon) for an address.
135138
139+
#### Props
140+
141+
- `address`: `string` - A valid [CAIP-10](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-10.md) address.
142+
- `size`: `string` - (Optional) The size of the avatar. Possible values are `"sm"`, `"md"`, and `"lg"`. The default is `"md"`.
143+
136144
:::note
137145
MetaMask automatically calculates checksums for EVM addresses (`eip155:`).
138146
Addresses for other namespaces are not validated; you should validate them in your Snap.
139147
:::
140148
141-
#### Props
142-
143-
- `address`: `string` - A valid [CAIP-10](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-10.md) address.
144-
145149
#### Example
146150
147151
```js
148152
export const onHomePage: OnHomePageHandler = async () => {
149153
return {
150154
content: (
151155
<Box>
152-
<Avatar address="eip155:1:0x1234567890123456789012345678901234567890" />
156+
<Avatar address="eip155:1:0x1234567890123456789012345678901234567890" size="lg" />
153157
<Avatar address="bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6" />
154158
</Box>
155159
),
156160
};
157161
};
158162
```
159163
164+
### `Banner`
165+
166+
Outputs a banner that can be used to display important messages with different severity levels.
167+
168+
#### Props
169+
170+
- `title`: `string` - The title of the banner.
171+
- `severity` - The severity level of the banner.
172+
Possible values are `"danger"`, `"info"`, `"success"`, and `"warning"`.
173+
- `children` - The content to display in the banner.
174+
This can include [`Text`](#text), [`Link`](#link) [`Icon`](#icon), [`Button`](#button), and [`Skeleton`](#skeleton) components.
175+
176+
#### Example
177+
178+
```javascript title="index.jsx"
179+
import { Box, Banner, Text, Link } from "@metamask/snaps-sdk/jsx";
180+
181+
await snap.request({
182+
method: "snap_dialog",
183+
params: {
184+
type: "alert",
185+
content: (
186+
<Box>
187+
<Banner title="Important Update" severity="info">
188+
<Text>A new version is available!</Text>
189+
<Link href="https://docs.metamask.io">Learn more</Link>
190+
</Banner>
191+
192+
<Banner title="Warning" severity="warning">
193+
<Text>Please review transaction details carefully.</Text>
194+
</Banner>
195+
196+
<Banner title="Success" severity="success">
197+
<Text>Transaction completed successfully.</Text>
198+
</Banner>
199+
200+
<Banner title="Error" severity="danger">
201+
<Text>Unable to process transaction.</Text>
202+
</Banner>
203+
</Box>
204+
),
205+
},
206+
});
207+
```
208+
160209
### `Bold`
161210
162211
Outputs bold text.
@@ -194,6 +243,8 @@ Outputs a box, which can be used as a container for other components.
194243
- `alignment` - (Optional) The alignment of the elements inside the box.
195244
Possible values are `"start"`, `"center"`, `"end"`, `"space-between"`, and `"space-around"`.
196245
The default is `"start"`.
246+
- `center`: `boolean` - (Optional) Whether to center the children within the box.
247+
The default is `false`.
197248
198249
#### Example
199250
@@ -208,6 +259,7 @@ module.exports.onHomePage = async () => {
208259
<Box
209260
direction="horizontal"
210261
alignment="space-around"
262+
center="true"
211263
>
212264
<Text>Feature 1</Text>
213265
<Text>Feature 2</Text>
@@ -343,8 +395,7 @@ Outputs a checkbox for use in [interactive UI](interactive-ui.md).
343395
- `name`: `string` - The name sent to [`onUserInput`](../../reference/entry-points.md#onuserinput).
344396
- `checked`: `boolean` - (Optional) Whether the checkbox is checked.
345397
- `label`: `string` - (Optional) The label for the checkbox.
346-
- `variant` - (Optional) The variant of the checkbox.
347-
Possible values are `"default"` and `"toggle"`.
398+
- `variant` - (Optional) The variant of the checkbox. Possible values are `"default"` and `"toggle"`.
348399
The default is `"default"`.
349400
350401
#### Example
@@ -369,6 +420,56 @@ const interfaceId = await snap.request({
369420
<img src={require("../../assets/custom-ui-checkbox.png").default} alt="Checkbox UI example" width="450px" style={{border: "1px solid #DCDCDC"}} />
370421
</p>
371422
423+
### `Container`
424+
425+
Outputs a container component that can hold a box of content and an optional footer. This is useful for creating structured layouts with action buttons at the bottom in [custom dialogs](dialogs.md#display-a-custom-dialog).
426+
427+
#### Props
428+
429+
- `backgroundColor` - (Optional) The background color of the container.
430+
Possible values are `"default"` and `"alternative"`.
431+
The default is `"default"`.
432+
- `children`: The contents of the container.
433+
This can be a single [`Box`](#box) component, or an array containing a [`Box`](#box) component and a [`Footer`](#footer) component.
434+
435+
#### Example
436+
437+
```javascript title="index.jsx"
438+
import { Container, Box, Footer, Text, Button } from "@metamask/snaps-sdk/jsx";
439+
440+
await snap.request({
441+
method: "snap_dialog",
442+
params: {
443+
content: (
444+
<Container backgroundColor="default">
445+
<Box>
446+
<Text>Are you sure you want to proceed with this transaction?</Text>
447+
<Text>Gas fees: 0.005 ETH</Text>
448+
</Box>
449+
<Footer>
450+
<Button name="cancel" variant="destructive">Cancel</Button>
451+
<Button name="confirm">Confirm</Button>
452+
</Footer>
453+
</Container>
454+
),
455+
},
456+
});
457+
458+
// Example without footer
459+
await snap.request({
460+
method: "snap_dialog",
461+
params: {
462+
content: (
463+
<Container backgroundColor="alternative">
464+
<Box>
465+
<Text>Simple container without footer</Text>
466+
</Box>
467+
</Container>
468+
),
469+
},
470+
});
471+
```
472+
372473
### `Copyable`
373474
374475
Outputs a read-only text field with a copy-to-clipboard shortcut.
@@ -576,6 +677,56 @@ export const onUserInput = async ({ id, event }) => {
576677
<img src={require("../../assets/custom-ui-file-input.png").default} alt="File input UI example" width="450px" style={{border: "1px solid #DCDCDC"}} />
577678
</p>
578679
680+
### `Footer`
681+
682+
Outputs a footer that can contain one or two buttons. This component is typically used within a [`Container`](#container) to create action buttons at the bottom of a dialog or panel.
683+
684+
#### Props
685+
686+
- `children` - The contents of the footer.
687+
This can be one or two [`Button`](#button) components.
688+
689+
#### Example
690+
691+
```javascript title="index.jsx"
692+
import { Container, Box, Footer, Text, Button } from "@metamask/snaps-sdk/jsx";
693+
694+
// Example with two buttons
695+
await snap.request({
696+
method: "snap_dialog",
697+
params: {
698+
content: (
699+
<Container>
700+
<Box>
701+
<Text>Delete this item?</Text>
702+
</Box>
703+
<Footer>
704+
<Button name="cancel">Cancel</Button>
705+
<Button name="delete" variant="destructive">Delete</Button>
706+
</Footer>
707+
</Container>
708+
),
709+
},
710+
});
711+
712+
// Example with single button
713+
await snap.request({
714+
method: "snap_dialog",
715+
params: {
716+
content: (
717+
<Container>
718+
<Box>
719+
<Text>Operation completed successfully.</Text>
720+
</Box>
721+
<Footer>
722+
<Button name="close">Close</Button>
723+
</Footer>
724+
</Container>
725+
),
726+
},
727+
});
728+
```
729+
579730
### `Form`
580731
581732
Outputs a form for use in [interactive UI](interactive-ui.md).
@@ -925,7 +1076,7 @@ Outputs a row with a label and value, which can be used for key-value data.
9251076
Possible values are `"default"`, `"error"`, and `"warning"`.
9261077
The default is `"default"`.
9271078
- `children` - The value of the row, which can be a [`Text`](#text), [`Image`](#image),
928-
[`Address`](#address), or [`Link`](#link) component.
1079+
[`Address`](#address), [`Link`](#link), or [`Value`](#value) component.
9291080

9301081
#### Example
9311082

@@ -1025,9 +1176,39 @@ const interfaceId = await snap.request({
10251176
<img src={require("../../assets/custom-ui-selector.png").default} alt="Selector UI example" width="450px" />
10261177
</p>
10271178

1179+
### `Skeleton`
1180+
1181+
Outputs an animated loading container.
1182+
1183+
#### Props
1184+
1185+
- `width`: `Array<number | string>` - (Optional) The width of the skeleton. The default is `"100%"`.
1186+
- `height`: `Array<number | string>` - (Optional) The height of the skeleton. The default is `22`.
1187+
- `borderRadius`: `string` - (Optional) The radius of the corners. Possible values are `"none"`, `"medium"` or `"full"`.
1188+
The default is `"medium"`.
1189+
1190+
#### Example
1191+
1192+
```javascript title="index.jsx"
1193+
import { Box, Heading, Skeleton } from "@metamask/snaps-sdk/jsx";
1194+
1195+
await snap.request({
1196+
method: "snap_dialog",
1197+
params: {
1198+
type: "alert",
1199+
content: (
1200+
<Box>
1201+
<Heading>Please wait...</Heading>
1202+
<Skeleton height={32} width="50%" borderRadius="full" />
1203+
</Box>
1204+
),
1205+
},
1206+
});
1207+
```
1208+
10281209
### `Spinner`
10291210

1030-
Outputs a loading indicator.
1211+
Outputs an animated loading indicator.
10311212

10321213
#### Example
10331214

@@ -1064,6 +1245,8 @@ Outputs text.
10641245
- `alignment` - (Optional) The alignment of the text.
10651246
Possible values are `"start"`, `"center"`, and `"end"`.
10661247
The default is `"start"`.
1248+
- `children` - The content to display.
1249+
This can include strings, and [`Bold`](#bold), [`Italic`](#italic), [`Icon`](#icon), [`Link`](#link), and [`Skeleton`](#skeleton) components.
10671250

10681251

10691252
#### Example
@@ -1126,6 +1309,53 @@ await snap.request({
11261309
<img src={require("../../assets/custom-ui-tooltip.png").default} alt="Tooltip UI example" width="450px" style={{border: "1px solid #DCDCDC"}} />
11271310
</p>
11281311

1312+
### `Value`
1313+
1314+
Outputs a component that displays two text values side by side. This component can only be used as a child of the [`Row`](#row) component.
1315+
1316+
#### Props
1317+
1318+
- `value` - The value shown on the right side.
1319+
This can be a string or a [`Text`](#text) component.
1320+
- `extra` - The extra text shown on the left side.
1321+
This can be a string or a [`Text`](#text) component.
1322+
1323+
#### Example
1324+
1325+
```javascript title="index.jsx"
1326+
import { Box, Row, Text, Value } from "@metamask/snaps-sdk/jsx";
1327+
1328+
await snap.request({
1329+
method: "snap_dialog",
1330+
params: {
1331+
type: "alert",
1332+
content: (
1333+
<Box>
1334+
<Row label="Transaction Amount">
1335+
<Value value="0.05 ETH" extra="$200" />
1336+
</Row>
1337+
1338+
{/* Example with styled text */}
1339+
<Row label="Gas Fee">
1340+
<Value
1341+
value={<Text color="error">0.003 ETH</Text>}
1342+
extra={<Text color="error">$12</Text>}
1343+
/>
1344+
</Row>
1345+
1346+
{/* Example with different text colors */}
1347+
<Row label="Balance After">
1348+
<Value
1349+
value={<Text color="success">1.25 ETH</Text>}
1350+
extra={<Text color="muted">$2,500</Text>}
1351+
/>
1352+
</Row>
1353+
</Box>
1354+
),
1355+
},
1356+
});
1357+
```
1358+
11291359
## Emojis
11301360

11311361
Text-based components (such as [`Heading`](#heading) and [`Text`](#text)) accept emojis.

0 commit comments

Comments
 (0)