diff --git a/packages/examples/packages/bip32/snap.manifest.json b/packages/examples/packages/bip32/snap.manifest.json
index dced6abb97..35a97a1475 100644
--- a/packages/examples/packages/bip32/snap.manifest.json
+++ b/packages/examples/packages/bip32/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "Myfiv00eKUdOQh/6VURJt/NLEHvF7t8p+arvI8ZEy2E=",
+ "shasum": "7AJkQic0kErNfOuBozGOUaJ5U5Z24yOV28zHL+WEPz0=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/bip44/snap.manifest.json b/packages/examples/packages/bip44/snap.manifest.json
index 0c1f0b6412..ed5acb595d 100644
--- a/packages/examples/packages/bip44/snap.manifest.json
+++ b/packages/examples/packages/bip44/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "kEd5v0Da+ONo/Pemo/W/+Rj+DCBcMlq8UoDkR3j+7hg=",
+ "shasum": "R/jtwKJg/6gTCuMrphuKHZkTtUlggfwnNgKiOXx11GU=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/browserify-plugin/snap.manifest.json b/packages/examples/packages/browserify-plugin/snap.manifest.json
index 1029c477be..d461c74fa0 100644
--- a/packages/examples/packages/browserify-plugin/snap.manifest.json
+++ b/packages/examples/packages/browserify-plugin/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "AtCtWmMVDhVL7nTsaqqCnm+M9tNQpPsp+i/w3G+dvK0=",
+ "shasum": "u7x/swxx6XHT7G0BDY8mJkwcY/mP/i4YmdYaRQ/QKZY=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/browserify/snap.manifest.json b/packages/examples/packages/browserify/snap.manifest.json
index df0aaa0662..e246e23315 100644
--- a/packages/examples/packages/browserify/snap.manifest.json
+++ b/packages/examples/packages/browserify/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "vMjY250Ko3ULV8mMenZFFKbTfHYY8HLQizQJkOK/Ee0=",
+ "shasum": "105XsceecyaQZ+reC6ZNusiOJVUMBoNSYK4PHvREPQw=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/client-status/snap.manifest.json b/packages/examples/packages/client-status/snap.manifest.json
index e227bb5819..5aef27eb43 100644
--- a/packages/examples/packages/client-status/snap.manifest.json
+++ b/packages/examples/packages/client-status/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "uo2UGVZKuD4tO104OeC7Y2oBkpLPrvJWWyGPJTWPVD4=",
+ "shasum": "9Ttl+TbR7gufQzhQx4o/keKeINCjio65rjLWOlP36NM=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/cronjobs/snap.manifest.json b/packages/examples/packages/cronjobs/snap.manifest.json
index 642b38627e..ff794a8fe1 100644
--- a/packages/examples/packages/cronjobs/snap.manifest.json
+++ b/packages/examples/packages/cronjobs/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "NuMOVeN5RPc/tuAINdZLD7Q0wccvhS9w0YLCq7JXs8M=",
+ "shasum": "0NNqpKgq/+zNokLs67+6vrA+kZxvDnDe9PWrX6X8Goc=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/dialogs/snap.manifest.json b/packages/examples/packages/dialogs/snap.manifest.json
index f0ba60b41c..a2a3144243 100644
--- a/packages/examples/packages/dialogs/snap.manifest.json
+++ b/packages/examples/packages/dialogs/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "7Zm7WDQ4Dvg8YnbqTacXu0oC3C25O1oMJkPnLW+r5co=",
+ "shasum": "tDbsxyvQUz2VoKmIzIXbLr3mtJfNewiHzoEjW7X6c8k=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/ethereum-provider/snap.manifest.json b/packages/examples/packages/ethereum-provider/snap.manifest.json
index ab6a9fb35a..79ee74c52d 100644
--- a/packages/examples/packages/ethereum-provider/snap.manifest.json
+++ b/packages/examples/packages/ethereum-provider/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "k0CibdCt3wsBIq47PPqzR193o2VjuddFrcOribmHHmc=",
+ "shasum": "8/P91GreWpqGOWLGAntmd3Kyu6A3WMelRIWUuxKcsGI=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/ethers-js/snap.manifest.json b/packages/examples/packages/ethers-js/snap.manifest.json
index 0cd225ecbf..afb4738193 100644
--- a/packages/examples/packages/ethers-js/snap.manifest.json
+++ b/packages/examples/packages/ethers-js/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "pFmqP6z84qAB39wGZcxFQifiNlDOKT7QBPMeKDKNRwg=",
+ "shasum": "1bLwZCxQusG/VCAswAJ5ohHF6fBgiQtTLhQG1Mby4HI=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/file-upload/snap.manifest.json b/packages/examples/packages/file-upload/snap.manifest.json
index 5b0ca54a5b..ff9b4d4c18 100644
--- a/packages/examples/packages/file-upload/snap.manifest.json
+++ b/packages/examples/packages/file-upload/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "OK/QqfPZc/L7VitrdRypHggyR4/4PnAG/j9erpJfJww=",
+ "shasum": "9m0//6LmoFrxb/Mpkjlr7eNRe9HK92iKJgtqtijlkFw=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/get-entropy/snap.manifest.json b/packages/examples/packages/get-entropy/snap.manifest.json
index 7321643b24..764d362693 100644
--- a/packages/examples/packages/get-entropy/snap.manifest.json
+++ b/packages/examples/packages/get-entropy/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "lFmdEJF/RldpN7/U6roJJm9BqSI5Bb/Whzh6dockQAw=",
+ "shasum": "sKg86tSHApZz49OdruONj63Yhts7zDlyLF2lpf6sguw=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/get-file/snap.manifest.json b/packages/examples/packages/get-file/snap.manifest.json
index 024f13679b..ba9cd98a97 100644
--- a/packages/examples/packages/get-file/snap.manifest.json
+++ b/packages/examples/packages/get-file/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "oX0FZa9+kdiEHrtU1sMbUCJMoXpiM9ur2W7WG8s/OW8=",
+ "shasum": "0vuiA3xbMMPYDMcE7ytDmXM0B4wwc+b90Ncy114JU24=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/home-page/snap.manifest.json b/packages/examples/packages/home-page/snap.manifest.json
index 1b4e69090f..34e5273135 100644
--- a/packages/examples/packages/home-page/snap.manifest.json
+++ b/packages/examples/packages/home-page/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "YV8OblLCDm1crxxmNDvWA3W8FL5Im3754++56NBSbUY=",
+ "shasum": "I2MgHF7MuMKoGGKMSJTB2TAdRNt3ThuAs7N5QLqsFwc=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/images/snap.manifest.json b/packages/examples/packages/images/snap.manifest.json
index 88d724b5d4..9de489cf9c 100644
--- a/packages/examples/packages/images/snap.manifest.json
+++ b/packages/examples/packages/images/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "wquuFPu3DiUvPAXvUsSwnYN6l8SltYivBAlMdfHXLS4=",
+ "shasum": "F+ZTNm0I7LeuhiRfFLz34sEn1nQrHEmZ4vbl4V1buiU=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/interactive-ui/snap.manifest.json b/packages/examples/packages/interactive-ui/snap.manifest.json
index 1f51775aa1..d0b3a94d16 100644
--- a/packages/examples/packages/interactive-ui/snap.manifest.json
+++ b/packages/examples/packages/interactive-ui/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "AYkzHZhTfVEuyZbMIOrbwX+fTYIm97M2rs/iwXEG954=",
+ "shasum": "LSk+GmGMP8byzHX013+Fnj8hPWxFD1ctKE7FwmXwUZ4=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/invoke-snap/packages/consumer-signer/snap.manifest.json b/packages/examples/packages/invoke-snap/packages/consumer-signer/snap.manifest.json
index ec3d881736..951f2c349d 100644
--- a/packages/examples/packages/invoke-snap/packages/consumer-signer/snap.manifest.json
+++ b/packages/examples/packages/invoke-snap/packages/consumer-signer/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "7mRu1KFdFY6XxJCVdUBj+vHOgiuEtdbuQ7pNXrwVZdw=",
+ "shasum": "gRoujvXbGzclQmIEgWQQvuKxeeTXt4jt8Kv+SggT5I8=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json b/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json
index 290cd9dde2..cf2c2e4664 100644
--- a/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json
+++ b/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "DMgBdNXhzY5eanbB+zfrshV+NfhYhwcf3N7+OpXOfVg=",
+ "shasum": "pgKvkVBqtJ4sPb6shDMNXiDc1C8apmV4+WfN6ciavKg=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/json-rpc/snap.manifest.json b/packages/examples/packages/json-rpc/snap.manifest.json
index a4ff828cc7..5e624673b7 100644
--- a/packages/examples/packages/json-rpc/snap.manifest.json
+++ b/packages/examples/packages/json-rpc/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "y06YrfQGNwFwo/kH3HfwJaWNYQu5C6UAKuZlafLWYHk=",
+ "shasum": "MDjrpUYVTAW1NxWp544Gn4DcF0t6GoDBQIR3F+zeqtI=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/jsx/snap.manifest.json b/packages/examples/packages/jsx/snap.manifest.json
index 1da343a63e..d188a8a74a 100644
--- a/packages/examples/packages/jsx/snap.manifest.json
+++ b/packages/examples/packages/jsx/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "6qUXiZopZ2r/V/Hd6v8v4OjP2IKpBwMu5WAZjO4Ikgs=",
+ "shasum": "G9fNyPGqUsjlqzMskFKEkdy04ny6eZDBBlXSajmQcN8=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/lifecycle-hooks/snap.manifest.json b/packages/examples/packages/lifecycle-hooks/snap.manifest.json
index 8f374bf06b..ec8df0fe47 100644
--- a/packages/examples/packages/lifecycle-hooks/snap.manifest.json
+++ b/packages/examples/packages/lifecycle-hooks/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "XKLkCOhCgU+yQ2Z+qnX7mSrlduxJeqMqBd5nqKKdz6Q=",
+ "shasum": "1Vo2b7dgicVZYWx6DMTlnkDnK6UBbk4fmilSsLKwSDg=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/localization/snap.manifest.json b/packages/examples/packages/localization/snap.manifest.json
index 05c2117df1..10ca7dc40f 100644
--- a/packages/examples/packages/localization/snap.manifest.json
+++ b/packages/examples/packages/localization/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "ZFIL9SrOEbTWaUDGIJZXZJPJFGraq3sPiWM8bC2tLy4=",
+ "shasum": "czQq93Uay+jE8HZvwSViZfMdDQTPn0ClMJaH34VN0dc=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/manage-state/snap.manifest.json b/packages/examples/packages/manage-state/snap.manifest.json
index 21adc7e4f1..230a3667b3 100644
--- a/packages/examples/packages/manage-state/snap.manifest.json
+++ b/packages/examples/packages/manage-state/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "lvCLamKc9tutH9zT6ECKjMoVgKxaueZxEm2Soy+GIX0=",
+ "shasum": "Vr0QhrtHYgg7DFczqQc4KEkAkKrOCOAc3ZnNesEVZ+w=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/network-access/snap.manifest.json b/packages/examples/packages/network-access/snap.manifest.json
index f6dbf4f567..dc4a0e6564 100644
--- a/packages/examples/packages/network-access/snap.manifest.json
+++ b/packages/examples/packages/network-access/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "osTfC/2Y6+aDcYtRj6N7YxVRLfVgNEHAmL7Km8nkwI4=",
+ "shasum": "6btrG6bcFAWosjSF/ldUuTd3sXNlYxVsRm0gcTJRE2M=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/notifications/snap.manifest.json b/packages/examples/packages/notifications/snap.manifest.json
index c501cb588b..b26add1a30 100644
--- a/packages/examples/packages/notifications/snap.manifest.json
+++ b/packages/examples/packages/notifications/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "xM6Q/SRD4ivSYzwqD2NhmlVQ5v2RHLnEN8ohxWtWcBU=",
+ "shasum": "khCrwAziXNhRJrV8cHp3hfRyudXVBYVB3Cew7YpWDG0=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/rollup-plugin/snap.manifest.json b/packages/examples/packages/rollup-plugin/snap.manifest.json
index 6ac46703ac..03ac09d8de 100644
--- a/packages/examples/packages/rollup-plugin/snap.manifest.json
+++ b/packages/examples/packages/rollup-plugin/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "9DHCPiYk3FZZqg0rdsH0YIJFL3x8BfUH3jcZAD2gqA4=",
+ "shasum": "7wOe/70O/A4BoUF5+CabV0Rjivnnv2D/xcQ1AlAZdw0=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/signature-insights/snap.manifest.json b/packages/examples/packages/signature-insights/snap.manifest.json
index bc64d3110a..d0e13487f4 100644
--- a/packages/examples/packages/signature-insights/snap.manifest.json
+++ b/packages/examples/packages/signature-insights/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "SBw2f5d1MMrve92+z+rGmPpJ5hJmyuMi1N6Pza/9nXU=",
+ "shasum": "5dDSK6C4XDIT1ukZh1nEimci2a+z0hnqUNEqi7qBNAk=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/transaction-insights/snap.manifest.json b/packages/examples/packages/transaction-insights/snap.manifest.json
index 8a6d8cdab6..3b2e3be783 100644
--- a/packages/examples/packages/transaction-insights/snap.manifest.json
+++ b/packages/examples/packages/transaction-insights/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "FBzAQuGVTVij6SLPkzro0OmcEPNXKT1NLHjiY3rUcxQ=",
+ "shasum": "8kKJRiaM18IxSr89svPvmzGIjRszgJvmfLkJxJLyFA4=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/wasm/snap.manifest.json b/packages/examples/packages/wasm/snap.manifest.json
index 686dd749ab..eb730f138c 100644
--- a/packages/examples/packages/wasm/snap.manifest.json
+++ b/packages/examples/packages/wasm/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "xEkfElvZ0ndhl1PcdkqDL6gicxdKxae/aKDl011Tgs4=",
+ "shasum": "DGgaRZmS8KSVnUzCq/V7f9TauAelSCFe9sg3myXrWks=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/examples/packages/webpack-plugin/snap.manifest.json b/packages/examples/packages/webpack-plugin/snap.manifest.json
index f6bc3fabda..54f4234e1c 100644
--- a/packages/examples/packages/webpack-plugin/snap.manifest.json
+++ b/packages/examples/packages/webpack-plugin/snap.manifest.json
@@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
- "shasum": "+x5V9w2ybgWUj2j/DvhKFRI3yo0ng8zhQ/BFN55F7yw=",
+ "shasum": "U4Zhw9ZpNVXJHoYfuYJ4a6j/ynAaFXxZYUYo0DSESVs=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
diff --git a/packages/snaps-controllers/coverage.json b/packages/snaps-controllers/coverage.json
index 263bcd6345..94c92cee99 100644
--- a/packages/snaps-controllers/coverage.json
+++ b/packages/snaps-controllers/coverage.json
@@ -1,6 +1,6 @@
{
- "branches": 91.98,
- "functions": 96.73,
+ "branches": 92.08,
+ "functions": 96.74,
"lines": 97.95,
"statements": 97.63
}
diff --git a/packages/snaps-controllers/src/interface/utils.test.tsx b/packages/snaps-controllers/src/interface/utils.test.tsx
index 59f41f9ae9..6cf808a508 100644
--- a/packages/snaps-controllers/src/interface/utils.test.tsx
+++ b/packages/snaps-controllers/src/interface/utils.test.tsx
@@ -8,6 +8,7 @@ import {
Input,
Text,
FileInput,
+ Checkbox,
} from '@metamask/snaps-sdk/jsx';
import { assertNameIsUnique, constructState } from './utils';
@@ -305,6 +306,53 @@ describe('constructState', () => {
});
});
+ it('supports root level checkboxes in forms', () => {
+ const element = (
+
+
+
+ );
+
+ const result = constructState({}, element);
+ expect(result).toStrictEqual({
+ foo: true,
+ });
+ });
+
+ it('sets default value for checkbox in forms', () => {
+ const element = (
+
+
+
+ );
+
+ const result = constructState({}, element);
+ expect(result).toStrictEqual({
+ form: { foo: false },
+ });
+ });
+
+ it('supports checkboxes in forms', () => {
+ const element = (
+
+
+
+ );
+
+ const result = constructState({}, element);
+ expect(result).toStrictEqual({
+ form: { foo: true },
+ });
+ });
+
it('supports nested fields', () => {
const element = (
diff --git a/packages/snaps-controllers/src/interface/utils.ts b/packages/snaps-controllers/src/interface/utils.ts
index f1965bbd08..d7ce6f1d4d 100644
--- a/packages/snaps-controllers/src/interface/utils.ts
+++ b/packages/snaps-controllers/src/interface/utils.ts
@@ -12,6 +12,7 @@ import type {
JSXElement,
OptionElement,
FileInputElement,
+ CheckboxElement,
} from '@metamask/snaps-sdk/jsx';
import { isJSXElementUnsafe } from '@metamask/snaps-sdk/jsx';
import {
@@ -60,14 +61,41 @@ export function assertNameIsUnique(state: InterfaceState, name: string) {
* @returns The default state for the specific component, if any.
*/
function constructComponentSpecificDefaultState(
- element: InputElement | DropdownElement,
+ element: InputElement | DropdownElement | CheckboxElement,
) {
- if (element.type === 'Dropdown') {
- const children = getJsxChildren(element) as OptionElement[];
- return children[0]?.props.value;
+ switch (element.type) {
+ case 'Dropdown': {
+ const children = getJsxChildren(element) as OptionElement[];
+ return children[0]?.props.value;
+ }
+
+ case 'Checkbox':
+ return false;
+
+ default:
+ return null;
}
+}
+
+/**
+ * Get the state value for a stateful component.
+ *
+ * Most components store the state value as a `value` prop.
+ * This function exists to account for components where that isn't the case.
+ *
+ * @param element - The input element.
+ * @returns The state value for a given component.
+ */
+function getComponentStateValue(
+ element: InputElement | DropdownElement | CheckboxElement,
+) {
+ switch (element.type) {
+ case 'Checkbox':
+ return element.props.checked;
- return null;
+ default:
+ return element.props.value;
+ }
}
/**
@@ -80,7 +108,7 @@ function constructComponentSpecificDefaultState(
*/
function constructInputState(
oldState: InterfaceState,
- element: InputElement | DropdownElement | FileInputElement,
+ element: InputElement | DropdownElement | FileInputElement | CheckboxElement,
form?: string,
) {
const oldStateUnwrapped = form ? (oldState[form] as FormState) : oldState;
@@ -91,7 +119,7 @@ function constructInputState(
}
return (
- element.props.value ??
+ getComponentStateValue(element) ??
oldInputState ??
constructComponentSpecificDefaultState(element) ??
null
@@ -135,7 +163,8 @@ export function constructState(
currentForm &&
(component.type === 'Input' ||
component.type === 'Dropdown' ||
- component.type === 'FileInput')
+ component.type === 'FileInput' ||
+ component.type === 'Checkbox')
) {
const formState = newState[currentForm.name] as FormState;
assertNameIsUnique(formState, component.props.name);
@@ -151,7 +180,8 @@ export function constructState(
if (
component.type === 'Input' ||
component.type === 'Dropdown' ||
- component.type === 'FileInput'
+ component.type === 'FileInput' ||
+ component.type === 'Checkbox'
) {
assertNameIsUnique(newState, component.props.name);
newState[component.props.name] = constructInputState(oldState, component);
diff --git a/packages/snaps-sdk/src/jsx/components/form/Checkbox.test.tsx b/packages/snaps-sdk/src/jsx/components/form/Checkbox.test.tsx
new file mode 100644
index 0000000000..7ac2a7557a
--- /dev/null
+++ b/packages/snaps-sdk/src/jsx/components/form/Checkbox.test.tsx
@@ -0,0 +1,33 @@
+import { Checkbox } from './Checkbox';
+
+describe('Checkbox', () => {
+ it('renders a checkbox', () => {
+ const result = ;
+
+ expect(result).toStrictEqual({
+ type: 'Checkbox',
+ props: {
+ name: 'foo',
+ checked: true,
+ },
+ key: null,
+ });
+ });
+
+ it('renders a checkbox with a variant and a label', () => {
+ const result = (
+
+ );
+
+ expect(result).toStrictEqual({
+ type: 'Checkbox',
+ props: {
+ name: 'foo',
+ checked: true,
+ variant: 'toggle',
+ label: 'Foo',
+ },
+ key: null,
+ });
+ });
+});
diff --git a/packages/snaps-sdk/src/jsx/components/form/Checkbox.ts b/packages/snaps-sdk/src/jsx/components/form/Checkbox.ts
new file mode 100644
index 0000000000..ad2dd799fc
--- /dev/null
+++ b/packages/snaps-sdk/src/jsx/components/form/Checkbox.ts
@@ -0,0 +1,41 @@
+import { createSnapComponent } from '../../component';
+
+/**
+ * The props of the {@link Checkbox} component.
+ *
+ * @property name - The name of the checkbox. This is used to identify the
+ * state in the form data.
+ * @property checked - Whether the checkbox is checked or not.
+ * @property label - An optional label for the checkbox.
+ * @property variant - An optional variant for the checkbox.
+ */
+export type CheckboxProps = {
+ name: string;
+ checked?: boolean | undefined;
+ label?: string | undefined;
+ variant?: 'default' | 'toggle' | undefined;
+};
+
+const TYPE = 'Checkbox';
+
+/**
+ * A checkbox component, which is used to create a checkbox.
+ *
+ * @param props - The props of the component.
+ * @param props.name - The name of the checkbox. This is used to identify the
+ * state in the form data.
+ * @param props.checked - Whether the checkbox is checked or not.
+ * @param props.label - An optional label for the checkbox.
+ * @param props.variant - An optional variant for the checkbox.
+ * @returns A checkbox element.
+ * @example
+ *
+ */
+export const Checkbox = createSnapComponent(TYPE);
+
+/**
+ * A checkbox element.
+ *
+ * @see Checkbox
+ */
+export type CheckboxElement = ReturnType;
diff --git a/packages/snaps-sdk/src/jsx/components/form/Field.ts b/packages/snaps-sdk/src/jsx/components/form/Field.ts
index 9ba260eed2..d2f87f38ba 100644
--- a/packages/snaps-sdk/src/jsx/components/form/Field.ts
+++ b/packages/snaps-sdk/src/jsx/components/form/Field.ts
@@ -1,5 +1,6 @@
import { createSnapComponent } from '../../component';
import type { ButtonElement } from './Button';
+import type { CheckboxElement } from './Checkbox';
import type { DropdownElement } from './Dropdown';
import type { FileInputElement } from './FileInput';
import type { InputElement } from './Input';
@@ -18,7 +19,8 @@ export type FieldProps = {
| [InputElement, ButtonElement]
| DropdownElement
| FileInputElement
- | InputElement;
+ | InputElement
+ | CheckboxElement;
};
const TYPE = 'Field';
diff --git a/packages/snaps-sdk/src/jsx/components/form/index.ts b/packages/snaps-sdk/src/jsx/components/form/index.ts
index af7ee70959..a2917bfaa5 100644
--- a/packages/snaps-sdk/src/jsx/components/form/index.ts
+++ b/packages/snaps-sdk/src/jsx/components/form/index.ts
@@ -1,4 +1,5 @@
import type { ButtonElement } from './Button';
+import type { CheckboxElement } from './Checkbox';
import type { DropdownElement } from './Dropdown';
import type { FieldElement } from './Field';
import type { FileInputElement } from './FileInput';
@@ -7,6 +8,7 @@ import type { InputElement } from './Input';
import type { OptionElement } from './Option';
export * from './Button';
+export * from './Checkbox';
export * from './Dropdown';
export * from './Option';
export * from './Field';
@@ -16,6 +18,7 @@ export * from './Input';
export type StandardFormElement =
| ButtonElement
+ | CheckboxElement
| FormElement
| FieldElement
| FileInputElement
diff --git a/packages/snaps-sdk/src/jsx/validation.test.tsx b/packages/snaps-sdk/src/jsx/validation.test.tsx
index eb6a2a0efa..98ccfff126 100644
--- a/packages/snaps-sdk/src/jsx/validation.test.tsx
+++ b/packages/snaps-sdk/src/jsx/validation.test.tsx
@@ -22,6 +22,7 @@ import {
Tooltip,
Value,
FileInput,
+ Checkbox,
} from './components';
import {
AddressStruct,
@@ -29,6 +30,7 @@ import {
BoldStruct,
BoxStruct,
ButtonStruct,
+ CheckboxStruct,
CopyableStruct,
DividerStruct,
DropdownStruct,
@@ -220,6 +222,9 @@ describe('FieldStruct', () => {
,
+
+
+ ,
])('validates a field element', (value) => {
expect(is(value, FieldStruct)).toBe(true);
});
@@ -466,6 +471,36 @@ describe('BoxStruct', () => {
});
});
+describe('CheckboxStruct', () => {
+ it.each([
+ ,
+ ,
+ ,
+ ])('validates a dropdown element', (value) => {
+ expect(is(value, CheckboxStruct)).toBe(true);
+ });
+
+ it.each([
+ 'foo',
+ 42,
+ null,
+ undefined,
+ {},
+ [],
+ // @ts-expect-error - Invalid props.
+ foo,
+ foo,
+
+ foo
+ ,
+
+
+
,
+ ])('does not validate "%p"', (value) => {
+ expect(is(value, CheckboxStruct)).toBe(false);
+ });
+});
+
describe('CopyableStruct', () => {
it.each([
,
@@ -541,9 +576,6 @@ describe('DropdownStruct', () => {
{},
[],
// @ts-expect-error - Invalid props.
- ,
- ,
- // @ts-expect-error - Invalid props.
foo,
foo,
@@ -553,7 +585,7 @@ describe('DropdownStruct', () => {
,
])('does not validate "%p"', (value) => {
- expect(is(value, ValueStruct)).toBe(false);
+ expect(is(value, DropdownStruct)).toBe(false);
});
});
diff --git a/packages/snaps-sdk/src/jsx/validation.ts b/packages/snaps-sdk/src/jsx/validation.ts
index 703cce5d77..1c6a2ccc7a 100644
--- a/packages/snaps-sdk/src/jsx/validation.ts
+++ b/packages/snaps-sdk/src/jsx/validation.ts
@@ -37,6 +37,7 @@ import type {
BoldElement,
BoxElement,
ButtonElement,
+ CheckboxElement,
CopyableElement,
DividerElement,
DropdownElement,
@@ -135,6 +136,16 @@ export const ButtonStruct: Describe = element('Button', {
disabled: optional(boolean()),
});
+/**
+ * A struct for the {@link CheckboxElement} type.
+ */
+export const CheckboxStruct: Describe = element('Checkbox', {
+ name: string(),
+ checked: optional(boolean()),
+ label: optional(string()),
+ variant: optional(nullUnion([literal('default'), literal('toggle')])),
+});
+
/**
* A struct for the {@link InputElement} type.
*/
@@ -187,6 +198,7 @@ export const FieldStruct: Describe = element('Field', {
DropdownStruct,
FileInputStruct,
InputStruct,
+ CheckboxStruct,
]),
});
@@ -396,6 +408,7 @@ export const BoxChildStruct = nullUnion([
SpinnerStruct,
TextStruct,
TooltipStruct,
+ CheckboxStruct,
]);
/**
@@ -429,6 +442,7 @@ export const JSXElementStruct: Describe = nullUnion([
OptionStruct,
ValueStruct,
TooltipStruct,
+ CheckboxStruct,
]);
/**
diff --git a/packages/snaps-sdk/src/types/handlers/user-input.test.ts b/packages/snaps-sdk/src/types/handlers/user-input.test.ts
index d9d914cd62..591b827f26 100644
--- a/packages/snaps-sdk/src/types/handlers/user-input.test.ts
+++ b/packages/snaps-sdk/src/types/handlers/user-input.test.ts
@@ -1,6 +1,10 @@
import { is } from 'superstruct';
-import { FormSubmitEventStruct, UserInputEventType } from './user-input';
+import {
+ FormSubmitEventStruct,
+ InputChangeEventStruct,
+ UserInputEventType,
+} from './user-input';
describe('UserInputEventType', () => {
it('has the correct values', () => {
@@ -13,7 +17,7 @@ describe('UserInputEventType', () => {
});
describe('FormSubmitEventStruct', () => {
- it('accepts string values and files', () => {
+ it('accepts strings, booleans, and files as value', () => {
expect(
is(
{
@@ -27,6 +31,7 @@ describe('FormSubmitEventStruct', () => {
contents: '...',
},
string: 'bar',
+ bool: true,
},
},
FormSubmitEventStruct,
@@ -34,3 +39,31 @@ describe('FormSubmitEventStruct', () => {
).toBe(true);
});
});
+
+describe('InputChangeEventStruct', () => {
+ it('accepts string values', () => {
+ expect(
+ is(
+ {
+ type: 'InputChangeEvent',
+ name: 'foo',
+ value: 'bar',
+ },
+ InputChangeEventStruct,
+ ),
+ ).toBe(true);
+ });
+
+ it('accepts boolean values', () => {
+ expect(
+ is(
+ {
+ type: 'InputChangeEvent',
+ name: 'foo',
+ value: true,
+ },
+ InputChangeEventStruct,
+ ),
+ ).toBe(true);
+ });
+});
diff --git a/packages/snaps-sdk/src/types/handlers/user-input.ts b/packages/snaps-sdk/src/types/handlers/user-input.ts
index 03db7c6704..126604d70f 100644
--- a/packages/snaps-sdk/src/types/handlers/user-input.ts
+++ b/packages/snaps-sdk/src/types/handlers/user-input.ts
@@ -9,6 +9,7 @@ import {
record,
string,
union,
+ boolean,
} from 'superstruct';
import type { InterfaceContext } from '../interface';
@@ -74,7 +75,7 @@ export const FormSubmitEventStruct = assign(
GenericEventStruct,
object({
type: literal(UserInputEventType.FormSubmitEvent),
- value: record(string(), nullable(union([string(), FileStruct]))),
+ value: record(string(), nullable(union([string(), FileStruct, boolean()]))),
name: string(),
}),
);
@@ -99,7 +100,7 @@ export const InputChangeEventStruct = assign(
object({
type: literal(UserInputEventType.InputChangeEvent),
name: string(),
- value: string(),
+ value: union([string(), boolean()]),
}),
);
diff --git a/packages/snaps-sdk/src/types/interface.ts b/packages/snaps-sdk/src/types/interface.ts
index b144c74601..624bc06b1a 100644
--- a/packages/snaps-sdk/src/types/interface.ts
+++ b/packages/snaps-sdk/src/types/interface.ts
@@ -1,6 +1,6 @@
import { JsonStruct } from '@metamask/utils';
import type { Infer } from 'superstruct';
-import { nullable, record, string, union } from 'superstruct';
+import { boolean, nullable, record, string, union } from 'superstruct';
import type { JSXElement } from '../jsx';
import { RootJSXElementStruct } from '../jsx';
@@ -15,7 +15,7 @@ import { FileStruct } from './handlers';
* either the value of an input or a sub-state of a form.
*/
-export const StateStruct = union([FileStruct, string()]);
+export const StateStruct = union([FileStruct, string(), boolean()]);
export const FormStateStruct = record(string(), nullable(StateStruct));