From 962774b57d02d4765e4f47cd5948db295057e42b Mon Sep 17 00:00:00 2001 From: fernandomg Date: Thu, 6 Jun 2024 22:48:59 +0200 Subject: [PATCH 1/3] chore(deps): reorg deps after install --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 39344826..531fee00 100644 --- a/package.json +++ b/package.json @@ -35,12 +35,12 @@ "@tanstack/react-query": "^5.36.0", "@tanstack/react-router": "^1.32.5", "connectkit": "^1.7.3", + "db-ui-toolkit": "github:BootNodeDev/db-ui-toolkit#0.0.5", "modern-normalize": "^2.0.0", "next-themes": "^0.3.0", "react": "^18.3.1", "react-dom": "^18.3.1", "styled-components": "^6.1.11", - "db-ui-toolkit": "github:BootNodeDev/db-ui-toolkit#0.0.5", "viem": "~2.10.9", "wagmi": "^2.9.2", "zod": "^3.23.8" From 84888f6f9119a6de8dcec0826ecaf5d21f28c3a4 Mon Sep 17 00:00:00 2001 From: fernandomg Date: Fri, 7 Jun 2024 00:50:53 +0200 Subject: [PATCH 2/3] feat(BigNumberInput): implement BigNumberInput component This is a copy of the `big-number-input` library, with modifications to avoid using BigNumber and migrate to the use of native BigInt. Tests were added to ensure that the minimum functionality is met. --- package.json | 4 + pnpm-lock.yaml | 485 +++++++++++++++++- setupTests.ts | 9 + .../web3/BigNumberInput.test.tsx | 121 +++++ src/sharedComponents/web3/BigNumberInput.tsx | 115 +++++ vite.config.ts | 6 +- 6 files changed, 734 insertions(+), 6 deletions(-) create mode 100644 setupTests.ts create mode 100644 src/sharedComponents/web3/BigNumberInput.test.tsx create mode 100644 src/sharedComponents/web3/BigNumberInput.tsx diff --git a/package.json b/package.json index 531fee00..f1077807 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,9 @@ "@tanstack/router-cli": "^1.32.2", "@tanstack/router-devtools": "^1.32.5", "@tanstack/router-vite-plugin": "^1.32.2", + "@testing-library/jest-dom": "^6.4.5", + "@testing-library/react": "^16.0.0", + "@testing-library/user-event": "^14.5.2", "@types/react": "^18.3.2", "@types/react-dom": "^18.3.0", "@typescript-eslint/eslint-plugin": "^7.9.0", @@ -69,6 +72,7 @@ "eslint-plugin-react-refresh": "^0.4.7", "eslint-plugin-sort-destructure-keys": "^2.0.0", "husky": "^9.0.11", + "jsdom": "^24.1.0", "lint-staged": "^15.2.2", "postcss-styled-syntax": "^0.6.4", "prettier": "^3.2.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c5544c97..f6fac76c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -66,6 +66,15 @@ importers: '@tanstack/router-vite-plugin': specifier: ^1.32.2 version: 1.32.2(vite@5.2.11(@types/node@20.12.12)(terser@5.31.0)) + '@testing-library/jest-dom': + specifier: ^6.4.5 + version: 6.4.5(vitest@1.6.0(@types/node@20.12.12)(jsdom@24.1.0(bufferutil@4.0.8))(terser@5.31.0)) + '@testing-library/react': + specifier: ^16.0.0 + version: 16.0.0(@testing-library/dom@10.1.0)(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@testing-library/user-event': + specifier: ^14.5.2 + version: 14.5.2(@testing-library/dom@10.1.0) '@types/react': specifier: ^18.3.2 version: 18.3.2 @@ -83,7 +92,7 @@ importers: version: 3.6.0(vite@5.2.11(@types/node@20.12.12)(terser@5.31.0)) '@vitest/coverage-v8': specifier: ^1.6.0 - version: 1.6.0(vitest@1.6.0(@types/node@20.12.12)(terser@5.31.0)) + version: 1.6.0(vitest@1.6.0(@types/node@20.12.12)(jsdom@24.1.0(bufferutil@4.0.8))(terser@5.31.0)) eslint: specifier: ^8.57.0 version: 8.57.0 @@ -117,6 +126,9 @@ importers: husky: specifier: ^9.0.11 version: 9.0.11 + jsdom: + specifier: ^24.1.0 + version: 24.1.0(bufferutil@4.0.8) lint-staged: specifier: ^15.2.2 version: 15.2.2 @@ -143,10 +155,13 @@ importers: version: 5.2.11(@types/node@20.12.12)(terser@5.31.0) vitest: specifier: ^1.6.0 - version: 1.6.0(@types/node@20.12.12)(terser@5.31.0) + version: 1.6.0(@types/node@20.12.12)(jsdom@24.1.0(bufferutil@4.0.8))(terser@5.31.0) packages: + '@adobe/css-tools@4.4.0': + resolution: {integrity: sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==} + '@adraffy/ens-normalize@1.10.0': resolution: {integrity: sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==} @@ -1890,6 +1905,55 @@ packages: '@tanstack/store@0.1.3': resolution: {integrity: sha512-GnolmC8Fr4mvsHE1fGQmR3Nm0eBO3KnZjDU0a+P3TeQNM/dDscFGxtA7p31NplQNW3KwBw4t1RVFmz0VeKLxcw==} + '@testing-library/dom@10.1.0': + resolution: {integrity: sha512-wdsYKy5zupPyLCW2Je5DLHSxSfbIp6h80WoHOQc+RPtmPGA52O9x5MJEkv92Sjonpq+poOAtUKhh1kBGAXBrNA==} + engines: {node: '>=18'} + + '@testing-library/jest-dom@6.4.5': + resolution: {integrity: sha512-AguB9yvTXmCnySBP1lWjfNNUwpbElsaQ567lt2VdGqAdHtpieLgjmcVyv1q7PMIvLbgpDdkWV5Ydv3FEejyp2A==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + peerDependencies: + '@jest/globals': '>= 28' + '@types/bun': latest + '@types/jest': '>= 28' + jest: '>= 28' + vitest: '>= 0.32' + peerDependenciesMeta: + '@jest/globals': + optional: true + '@types/bun': + optional: true + '@types/jest': + optional: true + jest: + optional: true + vitest: + optional: true + + '@testing-library/react@16.0.0': + resolution: {integrity: sha512-guuxUKRWQ+FgNX0h0NS0FIq3Q3uLtWVpBzcLOggmfMoUpgBnzBzvLLd4fbm6yS8ydJd94cIfY4yP9qUQjM2KwQ==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 + '@types/react-dom': ^18.0.0 + react: ^18.0.0 + react-dom: ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@testing-library/user-event@14.5.2': + resolution: {integrity: sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -2210,6 +2274,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.1: + resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} + engines: {node: '>= 14'} + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -2338,6 +2406,9 @@ packages: async-mutex@0.2.6: resolution: {integrity: sha512-Hs4R+4SPgamu6rSGW8C7cV9gaWUKEHykfzCCvIRuaVv636Ju10ZdeUbvb4TBEW0INuq2DHZqXbK4Nd3yG4RaRw==} + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + atomic-sleep@1.0.0: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} @@ -2490,6 +2561,10 @@ packages: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} + chalk@3.0.0: + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -2585,6 +2660,10 @@ packages: colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + command-exists@1.2.9: resolution: {integrity: sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==} @@ -2721,11 +2800,18 @@ packages: resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} hasBin: true + cssstyle@4.0.1: + resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==} + engines: {node: '>=18'} + csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -2736,6 +2822,10 @@ packages: resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} engines: {node: '>=12'} + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + data-view-buffer@1.0.1: resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} engines: {node: '>= 0.4'} @@ -2792,6 +2882,9 @@ packages: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} engines: {node: '>=0.10.0'} + decimal.js@10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + decode-uri-component@0.2.2: resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} engines: {node: '>=0.10'} @@ -2825,6 +2918,10 @@ packages: defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + denodeify@1.2.1: resolution: {integrity: sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg==} @@ -2870,6 +2967,12 @@ packages: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + dot-prop@5.3.0: resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} engines: {node: '>=8'} @@ -2919,6 +3022,10 @@ packages: resolution: {integrity: sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==} engines: {node: '>=10.13.0'} + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + env-paths@2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} @@ -3255,6 +3362,10 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + framer-motion@6.5.1: resolution: {integrity: sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==} peerDependencies: @@ -3453,6 +3564,10 @@ packages: hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -3467,10 +3582,18 @@ packages: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + http-shutdown@1.2.2: resolution: {integrity: sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + https-proxy-agent@7.0.4: + resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==} + engines: {node: '>= 14'} + human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -3490,6 +3613,10 @@ packages: i18next@22.5.1: resolution: {integrity: sha512-8TGPgM3pAD+VRsMtUMNknRz3kzqwp/gPALrWMsDnmC1mKqJwpWyooQRLMcbTwq8z8YwSmuj+ZYvc+xCuEpkssA==} + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + idb-keyval@6.2.1: resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==} @@ -3520,6 +3647,10 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} @@ -3670,6 +3801,9 @@ packages: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} @@ -3843,6 +3977,15 @@ packages: peerDependencies: '@babel/preset-env': ^7.1.6 + jsdom@24.1.0: + resolution: {integrity: sha512-6gpM7pRXCwIOKxX47cgOyvyQDN/Eh0f1MeKySBV2xGdKtqJBLj8P25eY3EVCWo2mglDDzozR2r2MW4T+JiNUZA==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^2.11.2 + peerDependenciesMeta: + canvas: + optional: true + jsesc@0.5.0: resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} hasBin: true @@ -4053,6 +4196,10 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + magic-string@0.30.10: resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} @@ -4193,6 +4340,10 @@ packages: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + minimalistic-assert@1.0.1: resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} @@ -4342,6 +4493,9 @@ packages: nullthrows@1.1.1: resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} + nwsapi@2.2.10: + resolution: {integrity: sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==} + ob1@0.80.9: resolution: {integrity: sha512-v9yOxowkZbxWhKOaaTyLjIm1aLy4ebMNcSn4NYJKOAI/Qv+SkfEfszpLr2GIxsccmb2Y2HA9qtsqiIJ80ucpVA==} engines: {node: '>=18'} @@ -4484,6 +4638,9 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse5@7.1.2: + resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} + parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} @@ -4632,6 +4789,10 @@ packages: resolution: {integrity: sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==} engines: {node: '>= 10'} + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + pretty-format@29.7.0: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -4655,6 +4816,9 @@ packages: proxy-compare@2.5.1: resolution: {integrity: sha512-oyfc0Tx87Cpwva5ZXezSp5V9vht1c7dZBhvuV/y3ctkgMVUmiAGDVeeB0dKhGSyT0v1ZTEQYpe/RXlBVBNuCLA==} + psl@1.9.0: + resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + pump@3.0.0: resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} @@ -4686,6 +4850,9 @@ packages: engines: {node: '>=0.4.x'} deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -4796,6 +4963,10 @@ packages: resolution: {integrity: sha512-hjMmLaUXAm1hIuTqOdeYObMslq/q+Xff6QE3Y2P+uoHAg2nmVlLBps2hzh1UJDdMtDTMXOFewK6ky51JQIeECg==} engines: {node: '>= 4'} + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + reflect.getprototypeof@1.0.6: resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} engines: {node: '>= 0.4'} @@ -4839,6 +5010,9 @@ packages: require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + resize-observer-polyfill@1.5.1: resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} @@ -4904,6 +5078,12 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rrweb-cssom@0.6.0: + resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} + + rrweb-cssom@0.7.0: + resolution: {integrity: sha512-KlSv0pm9kgQSRxXEMgtivPJ4h826YHsuob8pSHcfSZsSXGtvpEAie8S0AnXuObEJ7nhikOb4ahwxDm0H2yW17g==} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -4925,6 +5105,13 @@ packages: resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} engines: {node: '>=10'} + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} @@ -5167,6 +5354,10 @@ packages: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} engines: {node: '>=12'} + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -5250,6 +5441,9 @@ packages: svg-tags@1.0.0: resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + system-architecture@0.1.0: resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==} engines: {node: '>=18'} @@ -5330,9 +5524,17 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tr46@5.0.0: + resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} + engines: {node: '>=18'} + ts-api-utils@1.3.0: resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} engines: {node: '>=16'} @@ -5430,6 +5632,10 @@ packages: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} + universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} @@ -5494,6 +5700,9 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + use-sync-external-store@1.2.0: resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} peerDependencies: @@ -5623,6 +5832,10 @@ packages: resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} engines: {node: '>=0.10.0'} + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + wagmi@2.9.2: resolution: {integrity: sha512-FUSYm0RY2Zo7qL3LKDymtAk+oAiLJc0UUhfAEGhAgYBYqYXsDEpPoZM14i8zi6t4FMGlMONuyOTb0sediCJN1g==} peerDependencies: @@ -5646,9 +5859,25 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + whatwg-fetch@3.6.20: resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@14.0.0: + resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==} + engines: {node: '>=18'} + whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} @@ -5757,6 +5986,25 @@ packages: utf-8-validate: optional: true + ws@8.17.0: + resolution: {integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + xmlhttprequest-ssl@2.0.0: resolution: {integrity: sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==} engines: {node: '>=0.4.0'} @@ -5828,6 +6076,8 @@ packages: snapshots: + '@adobe/css-tools@4.4.0': {} + '@adraffy/ens-normalize@1.10.0': {} '@ampproject/remapping@2.3.0': @@ -7993,6 +8243,46 @@ snapshots: '@tanstack/store@0.1.3': {} + '@testing-library/dom@10.1.0': + dependencies: + '@babel/code-frame': 7.24.2 + '@babel/runtime': 7.24.5 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + chalk: 4.1.2 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + pretty-format: 27.5.1 + + '@testing-library/jest-dom@6.4.5(vitest@1.6.0(@types/node@20.12.12)(jsdom@24.1.0(bufferutil@4.0.8))(terser@5.31.0))': + dependencies: + '@adobe/css-tools': 4.4.0 + '@babel/runtime': 7.24.5 + aria-query: 5.3.0 + chalk: 3.0.0 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + lodash: 4.17.21 + redent: 3.0.0 + optionalDependencies: + vitest: 1.6.0(@types/node@20.12.12)(jsdom@24.1.0(bufferutil@4.0.8))(terser@5.31.0) + + '@testing-library/react@16.0.0(@testing-library/dom@10.1.0)(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.24.5 + '@testing-library/dom': 10.1.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.2 + '@types/react-dom': 18.3.0 + + '@testing-library/user-event@14.5.2(@testing-library/dom@10.1.0)': + dependencies: + '@testing-library/dom': 10.1.0 + + '@types/aria-query@5.0.4': {} + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.24.5 @@ -8184,7 +8474,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@20.12.12)(terser@5.31.0))': + '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@20.12.12)(jsdom@24.1.0(bufferutil@4.0.8))(terser@5.31.0))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 0.2.3 @@ -8199,7 +8489,7 @@ snapshots: std-env: 3.7.0 strip-literal: 2.1.0 test-exclude: 6.0.0 - vitest: 1.6.0(@types/node@20.12.12)(terser@5.31.0) + vitest: 1.6.0(@types/node@20.12.12)(jsdom@24.1.0(bufferutil@4.0.8))(terser@5.31.0) transitivePeerDependencies: - supports-color @@ -8639,6 +8929,12 @@ snapshots: acorn@8.11.3: {} + agent-base@7.1.1: + dependencies: + debug: 4.3.4(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -8794,6 +9090,8 @@ snapshots: dependencies: tslib: 2.6.2 + asynckit@0.4.0: {} + atomic-sleep@1.0.0: {} available-typed-arrays@1.0.7: @@ -8961,6 +9259,11 @@ snapshots: escape-string-regexp: 1.0.5 supports-color: 5.5.0 + chalk@3.0.0: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -9064,6 +9367,10 @@ snapshots: colorette@2.0.20: {} + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + command-exists@1.2.9: {} commander@11.1.0: {} @@ -9214,14 +9521,25 @@ snapshots: mdn-data: 2.0.30 source-map-js: 1.2.0 + css.escape@1.5.1: {} + cssesc@3.0.0: {} + cssstyle@4.0.1: + dependencies: + rrweb-cssom: 0.6.0 + csstype@3.1.3: {} damerau-levenshtein@1.0.8: {} dargs@8.1.0: {} + data-urls@5.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.0.0 + data-view-buffer@1.0.1: dependencies: call-bind: 1.0.7 @@ -9270,6 +9588,8 @@ snapshots: decamelize@1.2.0: {} + decimal.js@10.4.3: {} + decode-uri-component@0.2.2: {} deep-eql@4.1.3: @@ -9300,6 +9620,8 @@ snapshots: defu@6.1.4: {} + delayed-stream@1.0.0: {} + denodeify@1.2.1: {} depd@2.0.0: {} @@ -9330,6 +9652,10 @@ snapshots: dependencies: esutils: 2.0.3 + dom-accessibility-api@0.5.16: {} + + dom-accessibility-api@0.6.3: {} + dot-prop@5.3.0: dependencies: is-obj: 2.0.0 @@ -9394,6 +9720,8 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.2.1 + entities@4.5.0: {} + env-paths@2.2.1: {} envinfo@7.13.0: {} @@ -9915,6 +10243,12 @@ snapshots: dependencies: is-callable: 1.2.7 + form-data@4.0.0: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + framer-motion@6.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@motionone/dom': 10.12.0 @@ -10131,6 +10465,10 @@ snapshots: dependencies: react-is: 16.13.1 + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + html-escaper@2.0.2: {} html-parse-stringify@3.0.1: @@ -10147,8 +10485,22 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.1 + debug: 4.3.4(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + http-shutdown@1.2.2: {} + https-proxy-agent@7.0.4: + dependencies: + agent-base: 7.1.1 + debug: 4.3.4(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + human-signals@2.1.0: {} human-signals@5.0.0: {} @@ -10163,6 +10515,10 @@ snapshots: dependencies: '@babel/runtime': 7.24.5 + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + idb-keyval@6.2.1: {} ieee754@1.2.1: {} @@ -10187,6 +10543,8 @@ snapshots: imurmurhash@0.1.4: {} + indent-string@4.0.0: {} + inflight@1.0.6: dependencies: once: 1.4.0 @@ -10309,6 +10667,8 @@ snapshots: is-plain-object@5.0.0: {} + is-potential-custom-element-name@1.0.1: {} + is-regex@1.1.4: dependencies: call-bind: 1.0.7 @@ -10525,6 +10885,34 @@ snapshots: transitivePeerDependencies: - supports-color + jsdom@24.1.0(bufferutil@4.0.8): + dependencies: + cssstyle: 4.0.1 + data-urls: 5.0.0 + decimal.js: 10.4.3 + form-data: 4.0.0 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.4 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.10 + parse5: 7.1.2 + rrweb-cssom: 0.7.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 4.1.4 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.0.0 + ws: 8.17.0(bufferutil@4.0.8) + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + jsesc@0.5.0: {} jsesc@2.5.2: {} @@ -10755,6 +11143,8 @@ snapshots: dependencies: yallist: 3.1.1 + lz-string@1.5.0: {} + magic-string@0.30.10: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 @@ -10984,6 +11374,8 @@ snapshots: mimic-fn@4.0.0: {} + min-indent@1.0.1: {} + minimalistic-assert@1.0.1: {} minimalistic-crypto-utils@1.0.1: {} @@ -11099,6 +11491,8 @@ snapshots: nullthrows@1.1.1: {} + nwsapi@2.2.10: {} + ob1@0.80.9: {} obj-multiplex@1.0.0: @@ -11269,6 +11663,10 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse5@7.1.2: + dependencies: + entities: 4.5.0 + parseurl@1.3.3: {} path-exists@3.0.0: {} @@ -11390,6 +11788,12 @@ snapshots: ansi-styles: 4.3.0 react-is: 17.0.2 + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + pretty-format@29.7.0: dependencies: '@jest/schemas': 29.6.3 @@ -11417,6 +11821,8 @@ snapshots: proxy-compare@2.5.1: {} + psl@1.9.0: {} + pump@3.0.0: dependencies: end-of-stream: 1.4.4 @@ -11448,6 +11854,8 @@ snapshots: querystring@0.2.1: {} + querystringify@2.2.0: {} + queue-microtask@1.2.3: {} queue@6.0.2: @@ -11601,6 +12009,11 @@ snapshots: source-map: 0.6.1 tslib: 2.6.2 + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + reflect.getprototypeof@1.0.6: dependencies: call-bind: 1.0.7 @@ -11651,6 +12064,8 @@ snapshots: require-main-filename@2.0.0: {} + requires-port@1.0.0: {} + resize-observer-polyfill@1.5.1: {} resolve-from@3.0.0: {} @@ -11726,6 +12141,10 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.17.2 fsevents: 2.3.3 + rrweb-cssom@0.6.0: {} + + rrweb-cssom@0.7.0: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -11749,6 +12168,12 @@ snapshots: safe-stable-stringify@2.4.3: {} + safer-buffer@2.1.2: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + scheduler@0.23.2: dependencies: loose-envify: 1.4.0 @@ -12018,6 +12443,10 @@ snapshots: strip-final-newline@3.0.0: {} + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + strip-json-comments@3.1.1: {} strip-literal@2.1.0: @@ -12150,6 +12579,8 @@ snapshots: svg-tags@1.0.0: {} + symbol-tree@3.2.4: {} + system-architecture@0.1.0: {} table@6.8.2: @@ -12218,8 +12649,19 @@ snapshots: toidentifier@1.0.1: {} + tough-cookie@4.1.4: + dependencies: + psl: 1.9.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + tr46@0.0.3: {} + tr46@5.0.0: + dependencies: + punycode: 2.3.1 + ts-api-utils@1.3.0(typescript@5.4.5): dependencies: typescript: 5.4.5 @@ -12321,6 +12763,8 @@ snapshots: universalify@0.1.2: {} + universalify@0.2.0: {} + unpipe@1.0.0: {} unstorage@1.10.2(idb-keyval@6.2.1): @@ -12358,6 +12802,11 @@ snapshots: dependencies: punycode: 2.3.1 + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + use-sync-external-store@1.2.0(react@18.3.1): dependencies: react: 18.3.1 @@ -12457,7 +12906,7 @@ snapshots: fsevents: 2.3.3 terser: 5.31.0 - vitest@1.6.0(@types/node@20.12.12)(terser@5.31.0): + vitest@1.6.0(@types/node@20.12.12)(jsdom@24.1.0(bufferutil@4.0.8))(terser@5.31.0): dependencies: '@vitest/expect': 1.6.0 '@vitest/runner': 1.6.0 @@ -12481,6 +12930,7 @@ snapshots: why-is-node-running: 2.2.2 optionalDependencies: '@types/node': 20.12.12 + jsdom: 24.1.0(bufferutil@4.0.8) transitivePeerDependencies: - less - lightningcss @@ -12494,6 +12944,10 @@ snapshots: void-elements@3.1.0: {} + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + wagmi@2.9.2(@tanstack/query-core@5.36.1)(@tanstack/react-query@5.36.2(react@18.3.1))(@types/react@18.3.2)(bufferutil@4.0.8)(react-dom@18.3.1(react@18.3.1))(react-i18next@13.5.0(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react-native@0.74.1(@babel/core@7.24.5)(@babel/preset-env@7.24.5(@babel/core@7.24.5))(@types/react@18.3.2)(bufferutil@4.0.8)(react@18.3.1))(react@18.3.1))(react-native@0.74.1(@babel/core@7.24.5)(@babel/preset-env@7.24.5(@babel/core@7.24.5))(@types/react@18.3.2)(bufferutil@4.0.8)(react@18.3.1))(react@18.3.1)(rollup@4.17.2)(typescript@5.4.5)(viem@2.10.11(bufferutil@4.0.8)(typescript@5.4.5)(zod@3.23.8))(zod@3.23.8): dependencies: '@tanstack/react-query': 5.36.2(react@18.3.1) @@ -12544,8 +12998,21 @@ snapshots: webidl-conversions@3.0.1: {} + webidl-conversions@7.0.0: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + whatwg-fetch@3.6.20: {} + whatwg-mimetype@4.0.0: {} + + whatwg-url@14.0.0: + dependencies: + tr46: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 @@ -12655,6 +13122,14 @@ snapshots: optionalDependencies: bufferutil: 4.0.8 + ws@8.17.0(bufferutil@4.0.8): + optionalDependencies: + bufferutil: 4.0.8 + + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + xmlhttprequest-ssl@2.0.0: {} xtend@4.0.2: {} diff --git a/setupTests.ts b/setupTests.ts new file mode 100644 index 00000000..a97899eb --- /dev/null +++ b/setupTests.ts @@ -0,0 +1,9 @@ +import * as matchers from '@testing-library/jest-dom/matchers' +import { cleanup } from '@testing-library/react' +import { expect, afterEach } from 'vitest' + +expect.extend(matchers) + +afterEach(() => { + cleanup() +}) diff --git a/src/sharedComponents/web3/BigNumberInput.test.tsx b/src/sharedComponents/web3/BigNumberInput.test.tsx new file mode 100644 index 00000000..0503019c --- /dev/null +++ b/src/sharedComponents/web3/BigNumberInput.test.tsx @@ -0,0 +1,121 @@ +import { HTMLProps } from 'react' + +import { render, screen } from '@testing-library/react' +import { userEvent } from '@testing-library/user-event' +import '@testing-library/jest-dom' +import { describe, it, expect, vi } from 'vitest' + +import { BigNumberInput, BigNumberInputProps } from '@/src/sharedComponents/web3/BigNumberInput' + +// Mocking viem's parseUnits and formatUnits functions +vi.mock('viem', () => ({ + formatUnits: (value: bigint, decimals: number) => + (Number(value) / Math.pow(10, decimals)).toFixed(decimals), + parseUnits: (value: string, decimals: number) => + BigInt(Math.round(Number(value) * Math.pow(10, decimals))), + maxUint256: 2n ** 256n - 1n, +})) + +describe('BigNumberInput', () => { + const setup = (props: Partial = {}) => { + const defaultProps: BigNumberInputProps = { + decimals: 18, + onChange: vi.fn(), + value: '', + ...props, + } + + return { + user: userEvent.setup(), + ...render(), + } + } + + it('renders correctly with default props', () => { + setup() + expect(screen.getByPlaceholderText('0.00')).toBeInTheDocument() + }) + + it('handles autofocus correctly', () => { + setup({ autofocus: true }) + const input = screen.getByPlaceholderText('0.00') + expect(document.activeElement).toBe(input) + }) + + it('updates value onChange', async () => { + const handleChange = vi.fn() + const { user } = setup({ onChange: handleChange }) + + const input = screen.getByPlaceholderText('0.00') + input.focus() + await user.paste('1.23') + expect(handleChange).toHaveBeenCalledWith('1.23') + }) + + it('displays formatted value based on decimals', () => { + setup({ value: '1.234', decimals: 2 }) + const input = screen.getByPlaceholderText('0.00') + expect(input).toHaveValue('1.23') + }) + + it('triggers error on value less than min', async () => { + const handleError = vi.fn() + const { user } = setup({ min: '1.00', onError: handleError }) + + const input = screen.getByPlaceholderText('0.00') + input.focus() + await user.paste('0.50') + expect(handleError).toHaveBeenCalledWith({ + value: '0.50', + message: 'Invalid value! Range: [1.00, maxUint256] and value is: 0.50', + }) + }) + + it('triggers error on value more than max', async () => { + const handleError = vi.fn() + const { user } = setup({ max: '2.00', onError: handleError }) + + const input = screen.getByPlaceholderText('0.00') + input.focus() + await user.paste('3.00') + expect(handleError).toHaveBeenCalledWith({ + value: '3.00', + message: 'Invalid value! Range: [0, 2.00] and value is: 3.00', + }) + }) + + it('removes the error after correcting the value', async () => { + const handleError = vi.fn() + const { user } = setup({ max: '2.00', onError: handleError }) + + const input = screen.getByPlaceholderText('0.00') + input.focus() + await user.paste('3.00') + expect(handleError).toHaveBeenCalledWith({ + value: '3.00', + message: 'Invalid value! Range: [0, 2.00] and value is: 3.00', + }) + await user.paste('1.00') + expect(handleError).toHaveBeenCalledWith(null) + }) + + it('displays custom rendered input', () => { + const customRenderInput = (props: HTMLProps) => ( + + ) + setup({ renderInput: customRenderInput }) + expect(screen.getByTestId('custom-input')).toBeInTheDocument() + }) + + // TODO: fix test, or code? + it.skip('resets input value when cleared', async () => { + const handleChange = vi.fn() + setup({ onChange: handleChange, value: '1.123' }) + const user = userEvent.setup() + + const input = screen.getByPlaceholderText('0.00') + await user.clear(input) + expect(handleChange).toHaveBeenCalledWith('') + expect(input).toHaveValue('') + }) +}) diff --git a/src/sharedComponents/web3/BigNumberInput.tsx b/src/sharedComponents/web3/BigNumberInput.tsx new file mode 100644 index 00000000..f737839b --- /dev/null +++ b/src/sharedComponents/web3/BigNumberInput.tsx @@ -0,0 +1,115 @@ +import { ChangeEvent, HTMLProps, ReactElement, useEffect, useRef, useState } from 'react' + +import { formatUnits, parseUnits, maxUint256 } from 'viem' + +export type BigNumberInputProps = { + autofocus?: boolean + decimals: number + disabled?: boolean + max?: string + min?: string + onChange: (value: string) => void + onError?: (error: { value: string; message: string } | null) => void + placeholder?: string + renderInput?: (props: HTMLProps) => ReactElement + value: string +} + +export function BigNumberInput({ + autofocus, + decimals, + disabled, + max = maxUint256.toString(), + min = '0', + onChange, + onError, + placeholder = '0.00', + renderInput, + value, +}: BigNumberInputProps) { + const inputRef = useRef(null) + + const [inputValue, setInputvalue] = useState('') + + // update current value + useEffect(() => { + if (!value) { + setInputvalue('') + onError?.(null) + } else { + let parsedInputValue = 0n + let parsedValue = 0n + + try { + parsedInputValue = parseUnits(inputValue || '0', decimals) + parsedValue = parseUnits(value || '0', decimals) + } catch { + // do nothing + } + + if (parsedInputValue !== parsedValue) { + setInputvalue(formatUnits(parsedValue, decimals)) + onError?.(null) + } + } + }, [value, decimals, inputValue, onError]) + + // autofocus + useEffect(() => { + if (!renderInput && autofocus && inputRef.current) { + inputRef.current.focus() + } + }, [autofocus, inputRef, renderInput]) + + const updateValue = (event: ChangeEvent) => { + const { value } = event.currentTarget + + if (value === '') { + onChange(value) + setInputvalue(value) + return + } + + let newValue: bigint + try { + newValue = parseUnits(value, decimals) + } catch (e) { + // don't update the input on invalid values + return + } + + // this will fail when a value has no decimals, which is quite common + try { + const [, valueDecimals] = value.split('.') + + if (valueDecimals.length > decimals) { + return + } + } catch { + // fall-through + } + + const invalidValue = + (min && newValue < parseUnits(min, decimals)) || (max && newValue > parseUnits(max, decimals)) + + if (invalidValue) { + const message = `Invalid value! Range: [${min}, ${max === maxUint256.toString() ? 'maxUint256' : max}] and value is: ${value}` + console.warn(message) + onError?.({ value, message }) + } + + setInputvalue(value) + onChange(value) + !invalidValue && onError?.(null) + } + + const inputProps = { + disabled, + onChange: updateValue, + placeholder, + type: 'text', + value: inputValue, + } + + return renderInput ? renderInput({ ...inputProps }) : +} diff --git a/vite.config.ts b/vite.config.ts index 2715e04b..8e7eff09 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -14,5 +14,9 @@ export default defineConfig({ '@/src': path.resolve(__dirname, './src'), }, }, - test: {}, + test: { + environment: 'jsdom', + globals: true, + setupFiles: ['./setupTests.ts'], + }, }) From 8a602ad03a4e7c018d148a4d08f7c1248618cb52 Mon Sep 17 00:00:00 2001 From: fernandomg Date: Fri, 7 Jun 2024 00:51:49 +0200 Subject: [PATCH 3/3] feat(BigNumberInput): add demo component to /tokens page --- src/pageComponents/tokens/ADemoInput.tsx | 81 ++++++++++++++++++++++++ src/pageComponents/tokens/index.tsx | 20 ++++++ src/routeTree.gen.ts | 14 ++++ src/routes/tokens.lazy.tsx | 7 ++ src/sharedComponents/ui/MainMenu.tsx | 1 + 5 files changed, 123 insertions(+) create mode 100644 src/pageComponents/tokens/ADemoInput.tsx create mode 100644 src/pageComponents/tokens/index.tsx create mode 100644 src/routes/tokens.lazy.tsx diff --git a/src/pageComponents/tokens/ADemoInput.tsx b/src/pageComponents/tokens/ADemoInput.tsx new file mode 100644 index 00000000..3e645e48 --- /dev/null +++ b/src/pageComponents/tokens/ADemoInput.tsx @@ -0,0 +1,81 @@ +import { useState } from 'react' +import styled from 'styled-components' + +import { Button } from 'db-ui-toolkit' + +import { BigNumberInput } from '@/src/sharedComponents/web3/BigNumberInput' + +const Wrapper = styled.div` + display: flex; + flex-direction: column; + align-items: center; + position: relative; + row-gap: 6px; + width: 300px; +` + +const Error = styled.p` + text-align: center; + color: red; + font-weight: 400; +` + +const decimals = 6 +const max = '1.999998' +const min = '0.000001' + +export const ADemoInput = () => { + const [value, setValue] = useState('1.123') + const [disabled, setDisabled] = useState(false) + const [error, setError] = useState<{ value: string; message: string } | null>() + + const handleValueChange = (value: string) => { + console.log('value', value, typeof value) + setValue(value) + } + + const handleError = (error?: { value: string; message: string } | null) => { + setError(error) + } + + const toggleDisabled = () => { + setDisabled((prev) => !prev) + } + + return ( + + + <> + {error === undefined ? ( +

pristine

+ ) : error === null ? ( +

no error

+ ) : ( + 😱 {error.message} 😱 + )} + +
    +
  • decimals: {decimals}
  • +
  • max: {max}
  • +
  • min: {min}
  • +
+
+ +
+
+ +
+
+ +
+
+ ) +} diff --git a/src/pageComponents/tokens/index.tsx b/src/pageComponents/tokens/index.tsx new file mode 100644 index 00000000..80576623 --- /dev/null +++ b/src/pageComponents/tokens/index.tsx @@ -0,0 +1,20 @@ +import styled from 'styled-components' + +import { ADemoInput } from '@/src/pageComponents/tokens/ADemoInput' + +const Wrapper = styled.div` + display: flex; + flex-grow: 1; + justify-content: center; + flex-direction: column; + align-items: center; +` + +export const Tokens = () => { + return ( + +

Hello from Tokens!

+ +
+ ) +} diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index c13f896e..765df778 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -16,12 +16,18 @@ import { Route as rootRoute } from './routes/__root' // Create Virtual Routes +const TokensLazyImport = createFileRoute('/tokens')() const ContactLazyImport = createFileRoute('/contact')() const AboutLazyImport = createFileRoute('/about')() const IndexLazyImport = createFileRoute('/')() // Create/Update Routes +const TokensLazyRoute = TokensLazyImport.update({ + path: '/tokens', + getParentRoute: () => rootRoute, +} as any).lazy(() => import('./routes/tokens.lazy').then((d) => d.Route)) + const ContactLazyRoute = ContactLazyImport.update({ path: '/contact', getParentRoute: () => rootRoute, @@ -62,6 +68,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ContactLazyImport parentRoute: typeof rootRoute } + '/tokens': { + id: '/tokens' + path: '/tokens' + fullPath: '/tokens' + preLoaderRoute: typeof TokensLazyImport + parentRoute: typeof rootRoute + } } } @@ -71,6 +84,7 @@ export const routeTree = rootRoute.addChildren({ IndexLazyRoute, AboutLazyRoute, ContactLazyRoute, + TokensLazyRoute, }) /* prettier-ignore-end */ diff --git a/src/routes/tokens.lazy.tsx b/src/routes/tokens.lazy.tsx new file mode 100644 index 00000000..f9886208 --- /dev/null +++ b/src/routes/tokens.lazy.tsx @@ -0,0 +1,7 @@ +import { createLazyFileRoute } from '@tanstack/react-router' + +import { Tokens } from '@/src/pageComponents/tokens' + +export const Route = createLazyFileRoute('/tokens')({ + component: Tokens, +}) diff --git a/src/sharedComponents/ui/MainMenu.tsx b/src/sharedComponents/ui/MainMenu.tsx index e5033aaf..86569d3d 100644 --- a/src/sharedComponents/ui/MainMenu.tsx +++ b/src/sharedComponents/ui/MainMenu.tsx @@ -29,6 +29,7 @@ export const MainMenu = ({ ...restProps }) => { return ( Home + Tokens About Contact