diff --git a/docs/whats-new.md b/docs/whats-new.md index 9a26b94691..75e5e38654 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -11,6 +11,9 @@ of the [MetaMask developer page](https://metamask.io/developer/). ## September 2024 +- Documented new [Snaps custom UI JSX components](/snaps/features/custom-ui) for Flask + version 12.4, and removed documentation for deprecated function-based library. + ([#1540](https://github.com/MetaMask/metamask-docs/pull/1540)) - Documented [Snaps user-defined components](/snaps/features/custom-ui/user-defined-components). ([#1557](https://github.com/MetaMask/metamask-docs/pull/1557)) - Updated [Android SDK documentation](/wallet/how-to/use-sdk/mobile/android) with convenience diff --git a/snaps/assets/custom-ui-checkbox.png b/snaps/assets/custom-ui-checkbox.png new file mode 100644 index 0000000000..ff803115f8 Binary files /dev/null and b/snaps/assets/custom-ui-checkbox.png differ diff --git a/snaps/assets/custom-ui-file-input.png b/snaps/assets/custom-ui-file-input.png new file mode 100644 index 0000000000..7f69567c54 Binary files /dev/null and b/snaps/assets/custom-ui-file-input.png differ diff --git a/snaps/assets/custom-ui-heading.png b/snaps/assets/custom-ui-heading.png index b37b412321..8aee9f21cc 100644 Binary files a/snaps/assets/custom-ui-heading.png and b/snaps/assets/custom-ui-heading.png differ diff --git a/snaps/assets/custom-ui-icon.png b/snaps/assets/custom-ui-icon.png new file mode 100644 index 0000000000..ef6dbe6a3d Binary files /dev/null and b/snaps/assets/custom-ui-icon.png differ diff --git a/snaps/assets/custom-ui-radio-group.png b/snaps/assets/custom-ui-radio-group.png new file mode 100644 index 0000000000..0004c04f53 Binary files /dev/null and b/snaps/assets/custom-ui-radio-group.png differ diff --git a/snaps/assets/custom-ui-selector.png b/snaps/assets/custom-ui-selector.png new file mode 100644 index 0000000000..dc6d9a4457 Binary files /dev/null and b/snaps/assets/custom-ui-selector.png differ diff --git a/snaps/assets/custom-ui-tooltip.png b/snaps/assets/custom-ui-tooltip.png new file mode 100644 index 0000000000..6a1e00b1b5 Binary files /dev/null and b/snaps/assets/custom-ui-tooltip.png differ diff --git a/snaps/assets/home-page.png b/snaps/assets/home-page.png index 7b07f72d8f..8aee9f21cc 100644 Binary files a/snaps/assets/home-page.png and b/snaps/assets/home-page.png differ diff --git a/snaps/features/custom-ui/dialogs.md b/snaps/features/custom-ui/dialogs.md index 80831ad3d3..0cd167e729 100644 --- a/snaps/features/custom-ui/dialogs.md +++ b/snaps/features/custom-ui/dialogs.md @@ -3,9 +3,6 @@ description: Display custom alert, confirmation, or prompt screens in MetaMask. sidebar_position: 2 --- -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - # Dialogs You can display a dialog in the MetaMask UI using the @@ -38,9 +35,6 @@ To display an alert that can only be acknowledged, call [`snap_dialog`](../../reference/snaps-api.md#snap_dialog) with `type: "alert"`. The following example displays custom UI that alerts the user when something happens in the system: - - - ```tsx title="index.tsx" import { Box, Text, Heading } from "@metamask/snaps-sdk/jsx"; @@ -60,30 +54,6 @@ await snap.request({ // Code that should execute after the alert has been acknowledged. ``` - - - -```javascript title="index.js" -import { panel, text, heading } from "@metamask/snaps-sdk" - -await snap.request({ - method: "snap_dialog", - params: { - type: "alert", - content: panel([ - heading("Something happened in the system"), - text("The thing that happened is..."), - ]), - }, -}) - -// Code that should execute after the alert has been acknowledged. -``` - - - - -

Alert dialog example

@@ -95,9 +65,6 @@ To display a confirmation that can be accepted or rejected, call The following example displays custom UI that asks the user to confirm whether they would like to take an action: - - - ```tsx title="index.tsx" import { Box, Text, Heading } from "@metamask/snaps-sdk/jsx"; @@ -119,31 +86,6 @@ if (result === true) { } ``` - - - -```javascript title="index.js" -import { panel, text, heading } from "@metamask/snaps-sdk" - -const result = await snap.request({ - method: "snap_dialog", - params: { - type: "confirmation", - content: panel([ - heading("Would you like to take the action?"), - text("The action is..."), - ]), - }, -}) - -if (result === true) { - // Do the action. -} -``` - - - -

Confirmation dialog example

@@ -156,9 +98,6 @@ Prompt dialogs also accept a `placeholder` value that displays in the input fiel The following example displays custom UI that prompts the user to enter a wallet address: - - - ```tsx title="index.tsx" import { Box, Text, Heading } from "@metamask/snaps-sdk/jsx"; @@ -179,30 +118,6 @@ const walletAddress = await snap.request({ // walletAddress will be a string containing the address entered by the user. ``` - - - -```javascript title="index.js" -import { panel, text, heading } from "@metamask/snaps-sdk" - -const walletAddress = await snap.request({ - method: "snap_dialog", - params: { - type: "prompt", - content: panel([ - heading("What is the wallet address?"), - text("Please enter the wallet address to be monitored"), - ]), - placeholder: "0x123...", - }, -}) - -// walletAddress will be a string containing the address entered by the user. -``` - - - -

Prompt dialog example

diff --git a/snaps/features/custom-ui/home-pages.md b/snaps/features/custom-ui/home-pages.md index 7fd7fe0285..fe6555b7f8 100644 --- a/snaps/features/custom-ui/home-pages.md +++ b/snaps/features/custom-ui/home-pages.md @@ -3,9 +3,6 @@ description: Display a dedicated UI page in MetaMask for your Snap. sidebar_position: 3 --- -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - # Home pages You can display a dedicated UI, or "home page," for your Snap within MetaMask. @@ -35,9 +32,6 @@ MetaMask calls this method when a user selects your Snap name in the Snaps menu. The following example displays custom UI that welcomes the user to the Snap's home page: - - - ```tsx title="index.tsx" import type { OnHomePageHandler } from "@metamask/snaps-sdk"; import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx"; @@ -54,26 +48,6 @@ export const onHomePage: OnHomePageHandler = async () => { }; ``` - - - -```typescript title="index.ts" -import type { OnHomePageHandler } from "@metamask/snaps-sdk" -import { panel, text, heading } from "@metamask/snaps-sdk" - -export const onHomePage: OnHomePageHandler = async () => { - return { - content: panel([ - heading("Hello world!"), - text("Welcome to my Snap home page!"), - ]), - } -} -``` - - - -

Home page example

diff --git a/snaps/features/custom-ui/index.md b/snaps/features/custom-ui/index.md index 517e9c23f4..02d544ef6f 100644 --- a/snaps/features/custom-ui/index.md +++ b/snaps/features/custom-ui/index.md @@ -1,16 +1,11 @@ --- -description: Display custom user interface components. +description: Display custom user interface components using JSX. sidebar_position: 4 --- # Custom UI -:::caution -This version of custom UI is deprecated. If you're building a new Snaps project, -use [custom UI with JSX](./with-jsx). JSX is supported in MetaMask Extension and Flask version 12+. -::: - -You can display custom user interface (UI) components using the +You can display custom user interface (UI) JSX components using the [`@metamask/snaps-sdk`](https://github.com/MetaMask/snaps/tree/main/packages/snaps-sdk) module when implementing the following features: @@ -19,6 +14,10 @@ implementing the following features: - [Transaction insights](../transaction-insights.md) - [Signature insights](../signature-insights.md) +:::note +JSX is supported in the MetaMask extension and Flask version 12 and later. New UI components will be added as JSX components. The previous function-based library is deprecated. +::: + To use custom UI, first install [`@metamask/snaps-sdk`](https://github.com/MetaMask/snaps/tree/main/packages/snaps-sdk) using the following command: @@ -27,49 +26,57 @@ yarn add @metamask/snaps-sdk ``` Then, whenever you're required to return a custom UI component, import the components from the -SDK and build your UI with them. -For example, to display a [`panel`](#panel) using [`snap_dialog`](../../reference/snaps-api.md#snap_dialog): +SDK at `@metamask/snaps-sdk/jsx` and build your UI with them. +For example, to display a [`Box`](#box) using [`snap_dialog`](../../reference/snaps-api.md#snap_dialog): -```javascript title="index.js" -import { panel, heading, text } from "@metamask/snaps-sdk" +```javascript title="index.jsx" +import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx"; await snap.request({ method: "snap_dialog", params: { type: "alert", - content: panel([ - heading("Alert heading"), - text("Something happened in the system."), - ]), + content: ( + + Alert heading + Something happened in the system. + + ), }, -}) +}); ``` +:::note +JSX can only be used in `.jsx` or `.tsx` files. +::: + ## Components The following custom UI components are available: -### `address` +### `Address` -Outputs a formatted text field for an Ethereum address. -The address is automatically displayed with a jazzicon and truncated value. +Outputs a formatted text field for an Ethereum address. +The address is automatically displayed with a jazzicon and truncated value. Hovering the address shows the full value in a tooltip. #### Example -```javascript title="index.js" -import { panel, heading, address } from "@metamask/snaps-sdk" +```javascript title="index.jsx" +import { Box, Heading, Address } from "@metamask/snaps-sdk/jsx"; await snap.request({ method: "snap_dialog", params: { type: "alert", - content: panel([ - heading("Are you sure you want to send tokens to this address?"), - address("0x000000000000000000000000000000000000dEaD"), - ]), + content: ( + + Are you sure you want to send tokens to this address? +
+ + ), }, -}) +}); ```
@@ -81,41 +88,106 @@ await snap.request({
-### `button` +### `Bold` + +Outputs bold text. + +#### Example + +```javascript title="index.jsx" +import { Box, Heading, Text, Bold } from "@metamask/snaps-sdk/jsx"; + +await snap.request({ + method: "snap_dialog", + params: { + type: "alert", + content: ( + + Hello world! + + This is bold. + + + ), + }, +}); +``` + +### `Box` + +Outputs a box, which can be used as a container for other components. + +#### Props + +- `direction` - (Optional) The direction in which elements flow inside the box. + Possible values are `"horizontal"` and `"vertical"`. + The default is `"vertical"`. +- `alignment` - (Optional) The alignment of the elements inside the box. + Possible values are `"start"`, `"center"`, `"end"`, `"space-between"`, and `"space-around"`. + The default is `"start"`. + +#### Example + +```javascript title="index.jsx" +import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx"; + +module.exports.onHomePage = async () => { + return { + content: ( + + Features + + Feature 1 + Feature 2 + Feature 3 + + + ), + }; +}; +``` + +

+Box UI example +

+ +### `Button` Outputs a button that the user can select. For use in [interactive UI](interactive-ui.md). -#### Parameters - -An object containing: +#### Props -- `value`: `string` - The text of the button. -- `buttonType`: `string` - (Optional) Possible values are `button` or `submit`. - The default is `button`. +- `children` - The contents of the button. + This can be text, an [`Image`](#image) component, or an [`Icon`](#icon) component. +- `type` - (Optional) The type of button. + Possible values are `"button"` and `"submit"`. + The default is `"button"`. - `name`: `string` - (Optional) The name that will be sent to [`onUserInput`](../../reference/entry-points.md#onuserinput) when a user selects the button. - `variant` - (Optional) Determines the appearance of the button. - Possible values are `primary` or `secondary`. - The default is `primary`. + Possible values are `"primary"` and `"destructive"`. + The default is `"primary"`. #### Example ```javascript -import { button, panel, heading } from "@metamask/snaps-sdk" +import { Box, Heading, Button } from "@metamask/snaps-sdk/jsx"; const interfaceId = await snap.request({ method: "snap_createInterface", params: { - ui: panel([ - heading("Interactive interface"), - button({ - value: "Click me", - name: "interactive-button", - }), - ]), + ui: ( + + Interactive interface + + + ), }, -}) +}); await snap.request({ method: "snap_dialog", @@ -123,55 +195,99 @@ await snap.request({ type: "Alert", id: interfaceId, }, -}) +}); + ```

Button UI example

-### `copyable` +### `Checkbox` + +Outputs a checkbox for use in [interactive UI](interactive-ui.md). + +#### Props + +- `name`: `string` - The name sent to [`onUserInput`](../../reference/entry-points.md#onuserinput). +- `checked`: `boolean` - (Optional) Whether the checkbox is checked. +- `label`: `string` - (Optional) The label for the checkbox. +- `variant` - (Optional) The variant of the checkbox. + Possible values are `"default"` and `"toggle"`. + The default is `"default"`. + +#### Example + +```js +import { Checkbox } from "@metamask/snaps-sdk/jsx"; + +const interfaceId = await snap.request({ + method: "snap_createInterface", + params: { + ui: ( + + + + + ), + }, +}); +``` + +

+Checkbox UI example +

+ +### `Copyable` Outputs a read-only text field with a copy-to-clipboard shortcut. +#### Props + +- `value`: `string` - The value to copy when the user clicks on the copyable element. +- `sensitive`: `boolean` - (Optional) Indicates whether the value is sensitive. If `true`, the value will be hidden when the user is not interacting with the copyable element. + #### Example -```javascript title="index.js" -import { text, copyable } from "@metamask/snaps-sdk" +```javascript title="index.jsx" +import { Box, Text, Copyable } from "@metamask/snaps-sdk/jsx"; await snap.request({ method: "snap_dialog", params: { type: "alert", - content: panel([ - text("Your address:"), - copyable("0x000000000000000000000000000000000000dEaD"), - ]), + content: ( + + Your address: + + + ), }, -}) +}); ```

Copyable UI example

-### `divider` +### `Divider` Outputs a horizontal divider. #### Example -```javascript title="index.js" -import type { OnHomePageHandler } from "@metamask/snaps-sdk"; -import { panel, divider, text } from "@metamask/snaps-sdk"; +```javascript title="index.jsx" +import { Box, Heading, Divider, Text } from "@metamask/snaps-sdk/jsx"; module.exports.onHomePage = async () => { return { - content: panel([ - heading("Hello world!"), - divider(), - text("Welcome to my Snap home page!"), - ]), + content: ( + + Hello world! + + Welcome to my Snap home page! + + ), }; }; ``` @@ -180,41 +296,180 @@ module.exports.onHomePage = async () => { Divider UI example

-### `form` +### `Dropdown` -Outputs a form for use in [interactive UI](interactive-ui.md). +Outputs a dropdown for use in [interactive UI](interactive-ui.md). + +#### Props + +- `name`: `string` - The name sent to [`onUserInput`](../../reference/entry-points.md#onuserinput). +- `children`: `Option[]` - One or more `Option` components with the following props: + - `value`: `string` - The value sent to [`onUserInput`](../../reference/entry-points.md#onuserinput). + - `children`: `string` - The text displayed in the dropdown for that option. + +#### Example + +```js +import { Box, Text, Dropdown } from "@metamask/snaps-sdk/jsx"; -#### Parameters +const interfaceId = await snap.request({ + method: "snap_createInterface", + params: { + ui: ( + + Pick a currency + + + + + + ), + }, +}); -An object containing: +await snap.request({ + method: "snap_dialog", + params: { + type: "Alert", + id: interfaceId, + }, +}); +``` + +
+
+ Dropdown UI example +
+
+ Active dropdown UI example +
+
+ +### `Field` + +Outputs a form field, wrapping an element to give it a label and optional error. + +#### Props + +- `label`: `string` - The label for the wrapped element. +- `error`: `string` - (Optional) Any error for the wrapped element. Setting this changes the style of the wrapped element to show that there is an error. +- `children` - The element to be wrapped. + This can be a [`Dropdown`](#dropdown), [`Input`](#input), [`Selector`](#selector), or [`RadioGroup`](#radiogroup) component. + +#### Example + +```js +import { Field, Form, Input, Button } from "@metamask/snaps-sdk/jsx"; + +const interfaceId = await snap.request({ + method: "snap_createInterface", + params: { + ui: ( +
+ + + + +
+ ), + }, +}); + +await snap.request({ + method: "snap_dialog", + params: { + type: "Alert", + id: interfaceId, + }, +}); +``` + +

+Field example +

+ +### `FileInput` + +Outputs a file input component for use in [interactive UI](interactive-ui.md). + +#### Props - `name`: `string` - The name that will be sent to [`onUserInput`](../../reference/entry-points.md#onuserinput) when a user interacts with the form. -- `children`: `array` - An array of [`input`](#input) or [`button`](#button) components. +- `accept`: `string[]` - (Optional) The file types that the file input field accepts. If not +specified, the file input field accepts all file types. For examples of +valid values, see the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept). +- `compact`: `boolean` - (Optional) Whether the file input field is compact. #### Example ```js -import { input, button, form } from "@metamask/snaps-sdk" +import { FileInput } from "@metamask/snaps-sdk/jsx"; + +export const onHomePage = async () => { + const interfaceId = await snap.request({ + method: "snap_createInterface", + params: { + ui: ( + + File Upload +
+ + + + +
+
+ ), + }, + }); + + return { + id: interfaceId, + } +}; + +export const onUserInput = async ({ id, event }) => { + if (event.type === UserInputEventType.FileUploadEvent && event.file !== null) { + console.log(event.file); + } +}; +``` + +

+File input UI example +

+ +### `Form` + +Outputs a form for use in [interactive UI](interactive-ui.md). + +#### Props + +- `name`: `string` - The name that will be sent to [`onUserInput`](../../reference/entry-points.md#onuserinput) + when a user interacts with the form. +- `children`: `array` - An array of [`Input`](#input) or [`Button`](#button) components. + +#### Example + +```js +import { Form, Input, Button } from "@metamask/snaps-sdk/jsx"; const interfaceId = await snap.request({ method: "snap_createInterface", params: { - ui: form({ - name: "form-to-fill", - children: [ - input({ - name: "user-name", - placeholder: "Your name", - }), - button({ - value: "Submit", - buttonType: "submit", - }), - ], - }), + ui: ( +
+ + + + +
+ ), }, -}) +}); await snap.request({ method: "snap_dialog", @@ -222,30 +477,31 @@ await snap.request({ type: "Alert", id: interfaceId, }, -}) +}); ```

Form UI example

-### `heading` +### `Heading` Outputs a heading. -This is useful for [`panel`](#panel) titles. +This is useful for [`Box`](#box) titles. #### Example -```javascript title="index.js" -import type { OnHomePageHandler } from "@metamask/snaps-sdk"; -import { panel, heading, text } from "@metamask/snaps-sdk"; +```javascript title="index.jsx" +import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx"; module.exports.onHomePage = async () => { return { - content: panel([ - heading("Hello world!"), - text("Welcome to my Snap home page!"), - ]), + content: ( + + Hello world! + Welcome to my Snap home page! + + ), }; }; ``` @@ -254,14 +510,53 @@ module.exports.onHomePage = async () => { Divider UI example

-### `image` +### `Icon` + +Outputs an icon. + +#### Props + +- `name` - The name of the icon. + Possible values include `"confirmation"`, `"search"`, "`warning`", and `"menu"`. + See the [full list of possible `name` values](https://github.com/MetaMask/snaps/blob/0014ff6d7566dbc2945600db740d0a90f818b2d8/packages/snaps-sdk/src/jsx/components/Icon.ts#L5-L163). +- `color` - (Optional) The color of the icon. + Possible values are `"default"`, `"primary"`, and `"muted"`. + The default is `"default"`. +- `size` - (Optional) The size of the icon. + Possible values are `"md"` and `"inherit"`. + The default is `"md"`. -Outputs an image. +#### Example + +```javascript title="index.jsx" +import { Icon } from "@metamask/snaps-sdk/jsx"; + +await snap.request({ + method: "snap_dialog", + params: { + type: "alert", + content: ( + + + Double-check the "to" address before proceeding. + + ), + }, +}); +``` + +

+Icon UI example +

+ +### `Image` + +Outputs an image. This component takes an inline SVG. It does not support remote URLs. You can import SVG, PNG, and JPEG files using an import statement. -These files are automatically imported as SVG strings, so you can pass them directly to the `image` component. +These files are automatically imported as SVG strings, so you can pass them directly to the `Image` component. The SVG is rendered within an `` tag, which prevents JavaScript or interaction events from being supported. @@ -272,20 +567,26 @@ configuration option to `false`. The default is `true`. ::: +#### Props + +- `src`: `string` - An inline SVG. +- `alt`: `string` - An optional alternative text for the image. + #### Example -```javascript title="index.js" -import type { OnHomePageHandler } from "@metamask/snaps-sdk"; -import { panel, heading, text, image } from "@metamask/snaps-sdk"; +```javascript title="index.jsx" +import { Box, Heading, Text, Image } from "@metamask/snaps-sdk/jsx"; import svgIcon from "./path/to/icon.svg"; module.exports.onHomePage = async () => { return { - content: panel([ - heading("Hello world!"), - text("Welcome to my Snap home page!"), - image(svgIcon), - ]), + content: ( + + Hello world! + Welcome to my Snap home page! + + + ), }; }; ``` @@ -294,46 +595,44 @@ module.exports.onHomePage = async () => { Divider UI example

-### `input` +:::note +See the [`@metamask/images-example-snap`](https://github.com/MetaMask/snaps/tree/main/packages/examples/packages/images) +package for a full example of implementing images. +::: -Outputs an input component for use in [interactive UI](interactive-ui.md). +### `Input` -#### Parameters +Outputs an input component for use in [interactive UI](interactive-ui.md). -An object containing: +#### Props - `name`: `string` - The name that will be used as a key to the event sent to [`onUserInput`](../../reference/entry-points.md#onuserinput) when the containing form is submitted. -- `inputType`: `string` - (Optional) Type of input. - Possible values are `text`, `number`, or `password`. - The default is `text`. +- `type` - (Optional) The type of input. + Possible values are `"text"`, `"number"`, and `"password"`. + The default is `"text"`. - `placeholder`: `string` - (Optional) The text displayed when the input is empty. -- `label`: `string` (Optional) The text displayed alongside the input to label it. -- `value`: `string` (Optional) The default value of the input. +- `label`: `string` - (Optional) The text displayed alongside the input to label it. +- `value`: `string` - (Optional) The default value of the input. #### Example ```js -import { button, input, form } from "@metamask/snaps-sdk"; +import { Form, Input, Button } from "@metamask/snaps-sdk/jsx"; const interfaceId = await snap.request({ method: "snap_createInterface", params: { - ui: form({ - name: "form-to-fill", - children: [ - input({ - name: "user-name", - placeholder: "Your name", - }), - button({ - value: "Submit", - buttonType: "submit", - }), - ], - }), + ui: ( +
+ + + + +
+ ), }, -}) +}); await snap.request({ method: "snap_dialog", @@ -341,201 +640,363 @@ await snap.request({ type: "Alert", id: interfaceId, }, -}) +}); ```

Form UI example

-:::note -See the [`@metamask/images-example-snap`](https://github.com/MetaMask/snaps/tree/main/packages/examples/packages/images) -package for a full example of implementing images. -::: +### `Italic` + +Outputs italic text. + +#### Example + +```javascript title="index.jsx" +import { Box, Heading, Text, Italic } from "@metamask/snaps-sdk/jsx"; + +await snap.request({ + method: "snap_dialog", + params: { + type: "alert", + content: ( + + Hello world! + + This is italic. + + + ), + }, +}); +``` + +### `Link` -### `panel` +Outputs a clickable link. -Outputs a panel, which can be used as a container for other components. -This component takes an array of custom UI components. +#### Props + +- `href`: `string` - The URL to point to. This must be an HTTPS URL. +- `children`: `Array` - The link text. #### Example -```javascript title="index.js" -import type { OnTransactionHandler } from "@metamask/snaps-sdk"; -import { panel, heading, text } from "@metamask/snaps-sdk"; +```javascript title="index.jsx" +import { Box, Heading, Link, Text } from "@metamask/snaps-sdk/jsx"; -module.exports.onTransaction = async ({ transaction }) => { - const gasFeesPercentage = /* Calculate gas fees percentage */; +module.exports.onHomePage = async () => { return { - content: panel([ - heading("Transaction insights"), - text( - `As set up, you are paying **${gasFeesPercentage.toFixed( - 2, - )}%** in gas fees for this transaction.`, - ), - ]), + content: ( + + Hello world! + + Download MetaMask. + + + Read the MetaMask docs at MetaMask docs. + + + ), }; }; ```

-Panel UI example +Links UI example

-### `row` +### `RadioGroup` -Outputs a row with a label and value, which can be used for key-value data. -The label must be a string. The value can be a child component of type -[`text`](#text) or [`address`](#address). +Outputs a radio group component for use in [interactive UI](interactive-ui.md). + +#### Props + +- `name`: `string` - The name that will be used as a key to the event sent to + [`onUserInput`](../../reference/entry-points.md#onuserinput) when the containing form is submitted. +- `children`: `Radio[]` - One or more `Radio` components, each with a `value` and text child. #### Example -```javascript title="index.js" -import { panel, row, text, address } from "@metamask/snaps-sdk" +```js +import { RadioGroup, Radio } from "@metamask/snaps-sdk/jsx"; -await snap.request({ - method: "snap_dialog", +const interfaceId = await snap.request({ + method: "snap_createInterface", params: { - type: "alert", - content: panel([ - row("Address", address("0x000000000000000000000000000000000000dEaD")), - row("Balance", text("1.78 ETH")), - ]), + ui: ( +
+ + + Option 1 + Option 2 + + +
+ ), }, -}) +}); ```

-Row UI example +RadioGroup UI example

-### `spinner` +### `Row` -Outputs a loading indicator. +Outputs a row with a label and value, which can be used for key-value data. + +#### Props + +- `label`: `string` - The label of the row. +- `variant` - (Optional) The variant of the label. + Possible values are `"default"`, `"error"`, and `"warning"`. + The default is `"default"`. +- `children` - The value of the row, which can be a [`Text`](#text), [`Image`](#image), or + [`Address`](#address) component. #### Example -```javascript title="index.js" -import { panel, heading, spinner } from "@metamask/snaps-sdk" +```javascript title="index.jsx" +import { Box, Row, Text, Address } from "@metamask/snaps-sdk/jsx"; await snap.request({ method: "snap_dialog", params: { type: "alert", - content: panel([heading("Please wait..."), spinner()]), + content: ( + + +
+ + + 1.78 ETH + + + ), }, -}) +}); ```

-Spinner UI example +Row UI example

-### `text` +### `Selector` -Outputs text. +Outputs a selector component for use in [interactive UI](interactive-ui.md). + +#### Props + +- `name`: `string` - The name that will be used as a key to the event sent to + [`onUserInput`](../../reference/entry-points.md#onuserinput) when the containing form is submitted. +- `title`: `string` - The title of the selector, displayed when the selector is opened. +- `children`: `SelectorOption[]` - One or more `SelectorOption` components, each with a `Card` child. #### Example -```javascript title="index.js" -import type { OnHomePageHandler } from "@metamask/snaps-sdk"; -import { panel, heading, text } from "@metamask/snaps-sdk"; +```js +import { Selector, SelectorOption, Card } from "@metamask/snaps-sdk/jsx"; -module.exports.onHomePage = async () => { - return { - content: panel([ - heading("Hello world!"), - text("Welcome to my Snap home page!"), - ]), - }; -}; +const interfaceId = await snap.request({ + method: "snap_createInterface", + params: { + ui: ( + + + + + + + + + ), + }, +}); ```

-Text UI example +Selector UI example

-## Markdown +### `Spinner` -[`text`](#text) components accept bold and italic inline Markdown. +Outputs a loading indicator. #### Example -```javascript title="index.js" -import { panel, heading, text } from "@metamask/snaps-sdk" +```javascript title="index.jsx" +import { Box, Heading, Spinner } from "@metamask/snaps-sdk/jsx"; await snap.request({ method: "snap_dialog", params: { type: "alert", - content: panel([ - heading("Hello world!"), - text("This is **bold** and this is _italic_."), - ]), + content: ( + + Please wait... + + + ), }, -}) +}); ```

-Markdown UI example +Spinner UI example

-## Links +### `Text` + +Outputs text. + +#### Props + +- `color` - (Optional) The color of the text. + Possible values are `"default"`, "`alternative`", `"muted"`, `"error"`, `"success"`, and `"warning"`. + The default is `"default"`. +- `alignment` - (Optional) The alignment of the text. + Possible values are `"start"`, `"center"`, and `"end"`. + The default is `"start"`. -[`text`](#text) components accept inline links. #### Example -```javascript title="index.js" -import type { OnHomePageHandler } from "@metamask/snaps-sdk"; -import { panel, text } from "@metamask/snaps-sdk"; +```javascript title="index.jsx" +import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx"; module.exports.onHomePage = async () => { return { - content: panel([ - heading("Hello world!"), - text("Download [MetaMask](https://metamask.io)."), - text("Read the MetaMask docs at [](https://docs.metamask.io)."), - ]), + content: ( + + Hello world! + Welcome to my Snap home page! + + ), }; }; ```

-Links UI example +Text UI example +

+ +### `Tooltip` + +Outputs a tooltip when the wrapped element is hovered over. + +#### Props + +- `content`: - The content of the tooltip. +- `children`: - The element to wrap. + +#### Example + +```javascript title="index.jsx" +import { Tooltip, Text } from "@metamask/snaps-sdk/jsx"; + +await snap.request({ + method: "snap_dialog", + params: { + type: "alert", + content: ( + + + Tooltip text + + } + > + Hello world! + + + ), + }, +}); +``` + +

+Tooltip UI example

## Emojis -Text-based components (such as [`heading`](#heading) and [`text`](#text)) accept emojis. +Text-based components (such as [`Heading`](#heading) and [`Text`](#text)) accept emojis. #### Example -```javascript title="index.js" -import { panel, heading, text } from "@metamask/snaps-sdk" +```javascript title="index.jsx" +import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx"; await snap.request({ method: "snap_dialog", params: { type: "alert", - content: panel([ - heading("Hello world!"), - text("This is an apple 🍎 and this is an orange 🍊."), - ]), + content: ( + + Hello world! + This is an apple 🍎 and this is an orange 🍊. + + ), }, -}) +}); ```

Emojis UI example

-## Examples +## User-defined components + +In addition to the components provided by the SDK, you can [define your own components](user-defined-components.md). + +## Upgrade a Snap to use JSX + +If you have a Snap that uses the deprecated function-based custom UI library, follow these +steps to upgrade it to use JSX: + +1. Upgrade dependencies in `packages/snap/package.json`: + + - Upgrade `@metamask/snaps-sdk` to `^6.1.1` or later. + - Upgrade `@metamask/snaps-cli` to `^6.2.1` or later. + - Upgrade `@metamask/snaps-jest` to `^8.2.0` or later. + + Run `yarn install` to install the new versions. + +2. Update `packages/snap/.eslintrc.js`: + + - Add a new section in `overrides` with the following configuration: + ```json + { + "files": ["**/*.ts", "**/*.tsx"], + "extends": ["@metamask/eslint-config-typescript"], + "rules": { + // This allows importing the `Text` JSX component. + "@typescript-eslint/no-shadow": [ + "error", + { + "allow": ["Text"], + }, + ], + }, + } + ``` + - Replace `["*.test.ts"]` with `["*.test.ts", "*.test.tsx"]`. + +3. Update `packages/snap/src/index.ts`, if it will have JSX: + + - Rename the file to `index.tsx`. + - Modify the `input` field in `packages/snap/snap.config.ts` to `src/index.tsx`. + +4. Update `packages/snap/tsconfig.json`: -See the following packages for full examples of implementing custom UI: + - Under `compilerOptions`, add: + ```json + "jsx": "react-jsx", + "jsxImportSource": "@metamask/snaps-sdk" + ``` + - Change the `include` property from `["**/*.ts"]` to `["**/*.ts", "**/*.tsx"]`. -- [`@metamask/dialog-example-snap`](https://github.com/MetaMask/snaps/tree/main/packages/examples/packages/dialogs) -- [`@metamask/transaction-insight-example-snap`](https://github.com/MetaMask/snaps/tree/main/packages/examples/packages/transaction-insights) -- [`@metamask/home-page-example-snap`](https://github.com/MetaMask/snaps/tree/main/packages/examples/packages/home-page) -- [`@metamask/images-example-snap`](https://github.com/MetaMask/snaps/tree/main/packages/examples/packages/images) +5. Replace all custom UI in your code with JSX components, renaming the target files with the `.tsx` extension. diff --git a/snaps/features/custom-ui/interactive-ui.md b/snaps/features/custom-ui/interactive-ui.md index 7eb54e057d..6f0613cdac 100644 --- a/snaps/features/custom-ui/interactive-ui.md +++ b/snaps/features/custom-ui/interactive-ui.md @@ -12,17 +12,11 @@ It allows interfaces returned from [dialogs](dialogs.md), [home pages](home-page The following interactive UI components are available: -- [`button`](index.md#button) -- [`form`](index.md#form) -- [`input`](index.md#input) - -The following interactive UI JSX components are available: - -- [`Button`](with-jsx.md#button) -- [`Dropdown`](with-jsx.md#dropdown) -- [`Field`](with-jsx.md#field) -- [`Form`](with-jsx.md#form) -- [`Input`](with-jsx.md#input) +- [`Button`](index.md#button) +- [`Dropdown`](index.md#dropdown) +- [`Field`](index.md#field) +- [`Form`](index.md#form) +- [`Input`](index.md#input) ## Create an interactive interface @@ -37,6 +31,13 @@ If you need to [update the interface](#update-an-interactive-interface) or [get its state](#get-an-interactive-interfaces-state) at a future time, you should store its ID in the Snap's storage. +### Add context to an interface + +You can optionally add context to an interface by passing a `context` object to the +[`snap_createInterface`](../../reference/snaps-api.md#snap_createinterface) method. +This object can contain any data you want to pass to the interface. +This context will be passed to [`onUserInput`](../../reference/entry-points.md#onuserinput) when the user interacts with the interface. + ## Update an interactive interface To update an interactive interface that is still active, use the diff --git a/snaps/features/custom-ui/user-defined-components.md b/snaps/features/custom-ui/user-defined-components.md index b1b6b79c7a..a96cf2f3ff 100644 --- a/snaps/features/custom-ui/user-defined-components.md +++ b/snaps/features/custom-ui/user-defined-components.md @@ -5,7 +5,7 @@ sidebar_position: 5 # User-defined components -When using [Custom UI with JSX](with-jsx.md), you can create your own components by composing +When using [Custom UI with JSX](index.md), you can create your own components by composing existing components or other user-defined components. ## Basic example diff --git a/snaps/features/custom-ui/with-jsx.md b/snaps/features/custom-ui/with-jsx.md deleted file mode 100644 index 545ab1d638..0000000000 --- a/snaps/features/custom-ui/with-jsx.md +++ /dev/null @@ -1,748 +0,0 @@ ---- -description: Display custom user interface components using JSX. -sidebar_position: 4 ---- - -# Custom UI with JSX - -You can display custom user interface (UI) JSX components using the -[`@metamask/snaps-sdk`](https://github.com/MetaMask/snaps/tree/main/packages/snaps-sdk) module when -implementing the following features: - -- [Dialogs](dialogs.md) -- [Home pages](home-pages.md) -- [Transaction insights](../transaction-insights.md) -- [Signature insights](../signature-insights.md) - -:::note -JSX is supported in MetaMask Extension and Flask version 12+. New UI components will be added as JSX components. The previous function-based library is deprecated. -::: - -To use custom UI with JSX, first install [`@metamask/snaps-sdk`](https://github.com/MetaMask/snaps/tree/main/packages/snaps-sdk) -using the following command: - -```bash -yarn add @metamask/snaps-sdk -``` - -Then, whenever you're required to return a custom UI JSX component, import the components from the -SDK at `@metamask/snaps-sdk/jsx` and build your UI with them. -For example, to display a [`Box`](#box) (the [`panel`](./index.md#panel) function equivalent) using [`snap_dialog`](../../reference/snaps-api.md#snap_dialog): - -```javascript title="index.jsx" -import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx"; - -await snap.request({ - method: "snap_dialog", - params: { - type: "alert", - content: ( - - Alert heading - Something happened in the system. - - ), - }, -}); -``` - -:::note -Note that JSX can only be used in `.jsx` or `.tsx` files. -::: - -## Upgrade an existing Snap to use JSX - -Follow these steps to upgrade an existing Snap to use JSX: - -1. Upgrade dependencies in `packages/snap/package.json`: - - - Upgrade `@metamask/snaps-sdk` to `^6.1.1` or later. - - Upgrade `@metamask/snaps-cli` to `^6.2.1` or later. - - Upgrade `@metamask/snaps-jest` to `^8.2.0` or later. - - Run `yarn install` to install the new versions. - -2. Update `packages/snap/.eslintrc.js`: - - - Add a new section in `overrides` with the following configuration: - ```json - { - "files": ["**/*.ts", "**/*.tsx"], - "extends": ["@metamask/eslint-config-typescript"], - "rules": { - // This allows importing the `Text` JSX component. - "@typescript-eslint/no-shadow": [ - "error", - { - "allow": ["Text"], - }, - ], - }, - } - ``` - - Replace `["*.test.ts"]` with `["*.test.ts", "*.test.tsx"]`. - -3. Update `packages/snap/src/index.ts`, if it will have JSX: - - - Rename the file to `index.tsx`. - - Modify the `input` field in `packages/snap/snap.config.ts` to `src/index.tsx`. - -4. Update `packages/snap/tsconfig.json`: - - - Under `compilerOptions`, add: - ```json - "jsx": "react-jsx", - "jsxImportSource": "@metamask/snaps-sdk" - ``` - - Change the `include` property from `["**/*.ts"]` to `["**/*.ts", "**/*.tsx"]`. - -5. Replace all Custom UI in your code with JSX components, renaming the target files with the `.tsx` extension. - -## Components - -The following custom UI JSX components are available: - -### `Address` - -Outputs a formatted text field for an Ethereum address. -The address is automatically displayed with a jazzicon and truncated value. -Hovering the address shows the full value in a tooltip. - -#### Example - -```javascript title="index.jsx" -import { Box, Heading, Address } from "@metamask/snaps-sdk/jsx"; - -await snap.request({ - method: "snap_dialog", - params: { - type: "alert", - content: ( - - Are you sure you want to send tokens to this address? -
- - ), - }, -}); -``` - -
-
- Address UI example -
-
- Address tooltip UI example -
-
- -### `Bold` - -Outputs bold text. - -#### Example - -```javascript title="index.jsx" -import { Box, Heading, Text, Bold } from "@metamask/snaps-sdk/jsx"; - -await snap.request({ - method: "snap_dialog", - params: { - type: "alert", - content: ( - - Hello world! - - This is bold. - - - ), - }, -}); -``` - -### `Box` - -Outputs a box, which can be used as a container for other components. - -#### Props - -- `direction` - (Optional) The direction in which elements flow inside the box. - Possible values are `"horizontal"` or `"vertical"`. - The default is `"vertical"`. -- `alignment` - (Optional) The alignment of the elements inside the box. - Possible values are `"start"`, `"center"`, `"end"`, `"space-between"`, or `"space-around"`. - The default is `"start"`. - -#### Example - -```javascript title="index.jsx" -import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx"; - -module.exports.onHomePage = async () => { - return { - content: ( - - Features - - Feature 1 - Feature 2 - Feature 3 - - - ), - }; -}; -``` - -

-Box UI example -

- -### `Button` - -Outputs a button that the user can select. -For use in [interactive UI](interactive-ui.md). - -#### Props - -- `children`: `string` - The text of the button. -- `type` - (Optional) The type of button. - Possible values are `"button"` or `"submit"`. - The default is `"button"`. -- `name`: `string` - (Optional) The name that will be sent to [`onUserInput`](../../reference/entry-points.md#onuserinput) - when a user selects the button. -- `variant` - (Optional) Determines the appearance of the button. - Possible values are `"primary"` or `"destructive"`. - The default is `"primary"`. - -#### Example - -```javascript -import { Box, Heading, Button } from "@metamask/snaps-sdk/jsx"; - -const interfaceId = await snap.request({ - method: "snap_createInterface", - params: { - ui: ( - - Interactive interface - - - ), - }, -}); - -await snap.request({ - method: "snap_dialog", - params: { - type: "Alert", - id: interfaceId, - }, -}); - -``` - -

-Button UI example -

- -### `Copyable` - -Outputs a read-only text field with a copy-to-clipboard shortcut. - -#### Props - -- `value`: `string` - The value to copy when the user clicks on the copyable element. -- `sensitive`: `boolean` - (Optional) Indicates whether the value is sensitive. If `true`, the value will be hidden when the user is not interacting with the copyable element. - -#### Example - -```javascript title="index.jsx" -import { Box, Text, Copyable } from "@metamask/snaps-sdk/jsx"; - -await snap.request({ - method: "snap_dialog", - params: { - type: "alert", - content: ( - - Your address: - - - ), - }, -}); -``` - -

-Copyable UI example -

- -### `Divider` - -Outputs a horizontal divider. - -#### Example - -```javascript title="index.jsx" -import { Box, Heading, Divider, Text } from "@metamask/snaps-sdk/jsx"; - -module.exports.onHomePage = async () => { - return { - content: ( - - Hello world! - - Welcome to my Snap home page! - - ), - }; -}; -``` - -

-Divider UI example -

- -### `Dropdown` - -Outputs a dropdown for use in [interactive UI](interactive-ui.md). - -#### Props - -- `name`: `string` - The name sent to [`onUserInput`](../../reference/entry-points.md#onuserinput). -- `children`: `Option[]` - One or more `Option` components with the following props: - - `value`: `string` - The value sent to [`onUserInput`](../../reference/entry-points.md#onuserinput). - - `children`: `string` - The text displayed in the dropdown for that option. - -#### Example - -```js -import { Box, Text, Dropdown } from "@metamask/snaps-sdk/jsx"; - -const interfaceId = await snap.request({ - method: "snap_createInterface", - params: { - ui: ( - - Pick a currency - - - - - - ), - }, -}); - -await snap.request({ - method: "snap_dialog", - params: { - type: "Alert", - id: interfaceId, - }, -}); -``` - -
-
- Dropdown UI example -
-
- Active dropdown UI example -
-
- -### `Field` - -Outputs a form field, wrapping a [`Dropdown`](#dropdown) or [`Input`](#input) to give it a label and optional error. - -#### Props - -- `label`: `string` - The label for the wrapped element. -- `error`: `string` - Any error for the wrapped element. Setting this changes the styling of the wrapped element to show that there is an error. -- `children` - The [`Dropdown`](#dropdown) or [`Input`](#input) element to be wrapped. - -#### Example - -```js -import { Field, Form, Input, Button } from "@metamask/snaps-sdk/jsx"; - -const interfaceId = await snap.request({ - method: "snap_createInterface", - params: { - ui: ( -
- - - - -
- ), - }, -}); - -await snap.request({ - method: "snap_dialog", - params: { - type: "Alert", - id: interfaceId, - }, -}); -``` - -

-Field example -

- -### `Form` - -Outputs a form for use in [interactive UI](interactive-ui.md). - -#### Props - -- `name`: `string` - The name that will be sent to [`onUserInput`](../../reference/entry-points.md#onuserinput) - when a user interacts with the form. -- `children`: `array` - An array of [`Input`](#input) or [`Button`](#button) components. - -#### Example - -```js -import { Form, Input, Button } from "@metamask/snaps-sdk/jsx"; - -const interfaceId = await snap.request({ - method: "snap_createInterface", - params: { - ui: ( -
- - - - -
- ), - }, -}); - -await snap.request({ - method: "snap_dialog", - params: { - type: "Alert", - id: interfaceId, - }, -}); -``` - -

-Form UI example -

- -### `Heading` - -Outputs a heading. -This is useful for [`Box`](#box) titles. - -#### Example - -```javascript title="index.jsx" -import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx"; - -module.exports.onHomePage = async () => { - return { - content: ( - - Hello world! - Welcome to my Snap home page! - - ), - }; -}; -``` - -

-Divider UI example -

- -### `Image` - -Outputs an image. -This component takes an inline SVG. -It does not support remote URLs. - -You can import SVG, PNG, and JPEG files using an import statement. -These files are automatically imported as SVG strings, so you can pass them directly to the `Image` component. - -The SVG is rendered within an `` tag, which prevents JavaScript or interaction events from -being supported. - -:::note -To disable image support, set the [`features.images`](../../reference/cli/options.md#featuresimages) -configuration option to `false`. -The default is `true`. -::: - -#### Props - -- `src`: `string` - An inline SVG. -- `alt`: `string` - An optional alternative text for the image. - -#### Example - -```javascript title="index.jsx" -import { Box, Heading, Text, Image } from "@metamask/snaps-sdk/jsx"; -import svgIcon from "./path/to/icon.svg"; - -module.exports.onHomePage = async () => { - return { - content: ( - - Hello world! - Welcome to my Snap home page! - - - ), - }; -}; -``` - -

-Divider UI example -

- -:::note -See the [`@metamask/images-example-snap`](https://github.com/MetaMask/snaps/tree/main/packages/examples/packages/images) -package for a full example of implementing images. -::: - -### `Input` - -Outputs an input component for use in [interactive UI](interactive-ui.md). - -#### Props - -- `name`: `string` - The name that will be used as a key to the event sent to - [`onUserInput`](../../reference/entry-points.md#onuserinput) when the containing form is submitted. -- `type` - (Optional) The type of input. - Possible values are `"text"`, `"number"`, or `"password"`. - The default is `"text"`. -- `placeholder`: `string` - (Optional) The text displayed when the input is empty. -- `label`: `string` - (Optional) The text displayed alongside the input to label it. -- `value`: `string` - (Optional) The default value of the input. - -#### Example - -```js -import { Form, Input, Button } from "@metamask/snaps-sdk/jsx"; - -const interfaceId = await snap.request({ - method: "snap_createInterface", - params: { - ui: ( -
- - - - -
- ), - }, -}); - -await snap.request({ - method: "snap_dialog", - params: { - type: "Alert", - id: interfaceId, - }, -}); -``` - -

-Form UI example -

- -### `Italic` - -Outputs italic text. - -#### Example - -```javascript title="index.jsx" -import { Box, Heading, Text, Italic } from "@metamask/snaps-sdk/jsx"; - -await snap.request({ - method: "snap_dialog", - params: { - type: "alert", - content: ( - - Hello world! - - This is italic. - - - ), - }, -}); -``` - -### `Link` - -Outputs a clickable link. - -#### Props - -- `href`: `string` - The URL to point to. This must be an HTTPS URL. -- `children`: `Array` - The link text. - -#### Example - -```javascript title="index.jsx" -import { Box, Heading, Link, Text } from "@metamask/snaps-sdk/jsx"; - -module.exports.onHomePage = async () => { - return { - content: ( - - Hello world! - - Download MetaMask. - - - Read the MetaMask docs at MetaMask docs. - - - ), - }; -}; -``` - -

-Links UI example -

- -### `Row` - -Outputs a row with a label and value, which can be used for key-value data. - -#### Props - -- `label`: `string` - The label of the row. -- `variant` - (Optional) The variant of the label. - Possible values are `"default"`, `"error"`, or `"warning"`. -- `children` - The value of the row, which can be a [`Text`](#text), [`Image`](#image), or - [`Address`](#address) component. - -#### Example - -```javascript title="index.jsx" -import { Box, Row, Text, Address } from "@metamask/snaps-sdk/jsx"; - -await snap.request({ - method: "snap_dialog", - params: { - type: "alert", - content: ( - - -
- - - 1.78 ETH - - - ), - }, -}); -``` - -

-Row UI example -

- -### `Spinner` - -Outputs a loading indicator. - -#### Example - -```javascript title="index.jsx" -import { Box, Heading, Spinner } from "@metamask/snaps-sdk/jsx"; - -await snap.request({ - method: "snap_dialog", - params: { - type: "alert", - content: ( - - Please wait... - - - ), - }, -}); -``` - -

-Spinner UI example -

- -### `Text` - -Outputs text. - -#### Example - -```javascript title="index.jsx" -import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx"; - -module.exports.onHomePage = async () => { - return { - content: ( - - Hello world! - Welcome to my Snap home page! - - ), - }; -}; -``` - -

-Text UI example -

- -### Emojis - -Text-based components (such as [`Heading`](#heading) and [`Text`](#text)) accept emojis. - -#### Example - -```javascript title="index.jsx" -import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx"; - -await snap.request({ - method: "snap_dialog", - params: { - type: "alert", - content: ( - - Hello world! - This is an apple 🍎 and this is an orange 🍊. - - ), - }, -}); -``` - -

-Emojis UI example -

- -## User-defined components - -In addition to the components provided by the SDK, you can [define your own components](user-defined-components.md). diff --git a/snaps/index.mdx b/snaps/index.mdx index 843f95f089..c08e5c169b 100644 --- a/snaps/index.mdx +++ b/snaps/index.mdx @@ -36,6 +36,12 @@ The following Snaps features are available in the stable version of MetaMask: title: "Custom EVM accounts", description: "Connect to custom EVM accounts in MetaMask.", }, + { + icon: require("./assets/features/name-resolution.png").default, + href: "features/custom-name-resolution", + title: "Custom name resolution", + description: "Resolve names to addresses and vice versa.", + }, { icon: require("./assets/features/custom-ui.png").default, href: "features/custom-ui", @@ -133,13 +139,6 @@ the canary distribution of MetaMask: description: "Connect to ERC-4337 accounts in MetaMask.", flaskOnly: true, }, - { - icon: require("./assets/features/name-resolution.png").default, - href: "features/custom-name-resolution", - title: "Name resolution", - description: "Resolve names to addresses and vice versa.", - flaskOnly: true, - }, { icon: require("./assets/features/signature-insights.png").default, href: "features/signature-insights", diff --git a/snaps/reference/entry-points.md b/snaps/reference/entry-points.md index d965204d2b..dfddfa22a0 100644 --- a/snaps/reference/entry-points.md +++ b/snaps/reference/entry-points.md @@ -649,11 +649,12 @@ To respond to [interactive UI](../features/custom-ui/interactive-ui.md) events, - `id` - The ID of the interface being acted on. - `event` - An event object containing: - `type` - The type of the event. - Possible values are `ButtonClickEvent`, `FormSubmitEvent`, or `InputChangeEvent`. + Possible values are `ButtonClickEvent`, `FormSubmitEvent`, `InputChangeEvent`, and `FileInputEvent`. These enums are exported from the `@metamask/snaps-sdk` module. - `name` - The name of the component that fired the event. Optional when the event type is `ButtonClickEvent`. - `value` - When the event type is `FormSubmitEvent`, the values in the form as an object. +- `context` - The context object passed to the interface when calling [`snap_createInterface`](./snaps-api.md#snap_createinterface), or `null`. #### Example diff --git a/snaps/reference/snaps-api.md b/snaps/reference/snaps-api.md index d85837deee..270a43123e 100644 --- a/snaps/reference/snaps-api.md +++ b/snaps/reference/snaps-api.md @@ -860,6 +860,7 @@ Creates an interactive interface for use in [interactive UI](../features/custom- An object containing: - `ui` - The [custom UI](../features/custom-ui/index.md) to create. +- `context` (optional) - A custom context object that will be passed to [`onUserInput`](./entry-points.md#onuserinput) when the user interacts with the interface. #### Returns diff --git a/vercel.json b/vercel.json index 0c307a4f33..53fb82e6b7 100644 --- a/vercel.json +++ b/vercel.json @@ -402,6 +402,10 @@ "source": "/snaps/how-to/use-custom-ui/", "destination": "/snaps/features/custom-ui/" }, + { + "source": "/snaps/features/custom-ui/with-jsx/", + "destination": "/snaps/features/custom-ui/" + }, { "source": "/snaps/how-to/localize-a-snap/", "destination": "/snaps/features/localization/"