@getpara/react-sdk-lite 2.8.0 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/modal/ParaModal.js +1 -1
  2. package/dist/modal/components/Account/Account.js +17 -7
  3. package/dist/modal/components/Account/AccountProfile.js +30 -9
  4. package/dist/modal/components/Account/AccountSend/AccountSendForm.js +7 -1
  5. package/dist/modal/components/Account/AccountWallet.js +2 -0
  6. package/dist/modal/components/AddFunds/AddFunds.js +1 -4
  7. package/dist/modal/components/AddFunds/AddFundsAsset.js +25 -88
  8. package/dist/modal/components/AddFunds/AddFundsContext.d.ts +6 -2
  9. package/dist/modal/components/AddFunds/AddFundsContext.js +97 -15
  10. package/dist/modal/components/AddFunds/AddFundsNetwork.d.ts +1 -0
  11. package/dist/modal/components/AddFunds/AddFundsNetwork.js +31 -0
  12. package/dist/modal/components/AddFunds/AddFundsProvider.js +50 -65
  13. package/dist/modal/components/AddFunds/AddFundsReceive.js +4 -1
  14. package/dist/modal/components/AddFunds/AddFundsSettings.js +126 -143
  15. package/dist/modal/components/AuthInput/AuthInput.js +12 -1
  16. package/dist/modal/components/AuthMainStep/AuthMainStepContent.js +32 -11
  17. package/dist/modal/components/BiometricCreationStep/BiometricCreationStep.js +17 -8
  18. package/dist/modal/components/BiometricLoginStep/BiometricLoginStep.js +25 -9
  19. package/dist/modal/components/Body/Body.js +3 -51
  20. package/dist/modal/components/ExternalWalletStep/ExternalWalletStep.js +25 -8
  21. package/dist/modal/components/ExternalWallets/ExternalWallets.js +33 -13
  22. package/dist/modal/components/IFrameStep/IFrameStep.js +1 -1
  23. package/dist/modal/components/OAuth/OAuth.js +5 -3
  24. package/dist/modal/components/OnRampComponents/OnRampProviderButton.js +15 -24
  25. package/dist/modal/components/QuantityInput.js +62 -9
  26. package/dist/modal/components/RecoverySecretStep/RecoverySecretStep.js +4 -4
  27. package/dist/modal/components/SearchableButtonList.d.ts +2 -1
  28. package/dist/modal/components/SearchableButtonList.js +3 -1
  29. package/dist/modal/components/Setup2FAStep/Setup2FAStep.js +6 -5
  30. package/dist/modal/components/VerificationCodeStep/VerificationCodeStep.js +4 -2
  31. package/dist/modal/components/WalletCreationDoneStep/WalletCreationDoneStep.js +2 -2
  32. package/dist/modal/components/common.d.ts +1 -1
  33. package/dist/modal/stores/modal/actions.js +2 -1
  34. package/dist/modal/stores/modal/useModalStore.d.ts +2 -0
  35. package/dist/modal/stores/modal/useModalStore.js +2 -1
  36. package/dist/modal/utils/validatePortalOrigin.js +6 -0
  37. package/dist/provider/ParaProviderMin.js +3 -0
  38. package/package.json +8 -8
  39. package/dist/modal/components/AddFunds/common.d.ts +0 -5
  40. package/dist/modal/components/AddFunds/common.js +0 -17
@@ -26,19 +26,35 @@ const BiometricLoginStep = () => {
26
26
  /* @__PURE__ */ jsx(UserIdentifier, { authInfo: para.authInfo })
27
27
  ] }),
28
28
  /* @__PURE__ */ jsxs(MainContainer, { children: [
29
- (isPassword || isPIN) && /* @__PURE__ */ jsxs(CpslButton, { fullWidth: true, onClick: () => presentLoginUi(isPIN ? AuthMethod.PIN : AuthMethod.PASSWORD, loginState), children: [
30
- /* @__PURE__ */ jsx(CpslIcon, { slot: "start", icon: "passcode" }),
31
- isPIN && isPassword ? "Login" : isPIN ? "Login with PIN" : "Login with Password"
32
- ] }),
29
+ (isPassword || isPIN) && /* @__PURE__ */ jsxs(
30
+ CpslButton,
31
+ {
32
+ fullWidth: true,
33
+ onClick: () => presentLoginUi(isPIN ? AuthMethod.PIN : AuthMethod.PASSWORD, loginState),
34
+ "data-testid": "para-login-password",
35
+ children: [
36
+ /* @__PURE__ */ jsx(CpslIcon, { slot: "start", icon: "passcode" }),
37
+ isPIN && isPassword ? "Login" : isPIN ? "Login with PIN" : "Login with Password"
38
+ ]
39
+ }
40
+ ),
33
41
  isPasskey && /* @__PURE__ */ jsxs(Fragment, { children: [
34
42
  displayKnownDevices && /* @__PURE__ */ jsxs(Fragment, { children: [
35
- /* @__PURE__ */ jsx(KnownDevices, { hints: biometricHints, link: passkeyKnownDeviceUrl }),
43
+ /* @__PURE__ */ jsx(KnownDevices, { hints: biometricHints, link: passkeyKnownDeviceUrl, "data-testid": "para-known-devices" }),
36
44
  /* @__PURE__ */ jsx(CpslDivider, { children: "or" })
37
45
  ] }),
38
- /* @__PURE__ */ jsx(CpslButton, { fullWidth: true, onClick: () => presentLoginUi(AuthMethod.PASSKEY, loginState), children: isPasskeyUnavailable ? "Continue anyway" : /* @__PURE__ */ jsxs(Fragment, { children: [
39
- /* @__PURE__ */ jsx(CpslIcon, { slot: "start", icon: "key" }),
40
- "Login with Passkey"
41
- ] }) })
46
+ /* @__PURE__ */ jsx(
47
+ CpslButton,
48
+ {
49
+ fullWidth: true,
50
+ onClick: () => presentLoginUi(AuthMethod.PASSKEY, loginState),
51
+ "data-testid": "para-login-passkey",
52
+ children: isPasskeyUnavailable ? "Continue anyway" : /* @__PURE__ */ jsxs(Fragment, { children: [
53
+ /* @__PURE__ */ jsx(CpslIcon, { slot: "start", icon: "key" }),
54
+ "Login with Passkey"
55
+ ] })
56
+ }
57
+ )
42
58
  ] })
43
59
  ] })
44
60
  ] });
@@ -3,7 +3,6 @@ import "../../../chunk-MMUBH76A.js";
3
3
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
4
  import { safeStyled, WarningBanner, BODY_MOTION_VARIANTS, BODY_TRANSITION, MOBILE_SIZE } from "@getpara/react-common";
5
5
  import { IFrameSteps, ModalStep } from "../../utils/steps.js";
6
- import { CpslAlert, CpslIcon } from "@getpara/react-components";
7
6
  import { VerificationCodeStep } from "../VerificationCodeStep/VerificationCodeStep.js";
8
7
  import { useModalStore } from "../../stores/index.js";
9
8
  import { BiometricLoginStep } from "../BiometricLoginStep/BiometricLoginStep.js";
@@ -29,7 +28,7 @@ import { AnimatedHeightWrapper } from "./AnimatedHeightWrapper.js";
29
28
  import { ChainSwitch } from "../ChainSwitch/ChainSwitch.js";
30
29
  import { motion, AnimatePresence } from "framer-motion";
31
30
  import { Controls } from "../Controls/Controls.js";
32
- import { useEffect, useState } from "react";
31
+ import { useEffect } from "react";
33
32
  import { TelegramOAuthStep } from "../OAuth/TelegramOAuthStep.js";
34
33
  import { AwaitingPasswordStep } from "../AwaitingPasswordStep/AwaitingPasswordStep.js";
35
34
  import { IFrameStep } from "../IFrameStep/IFrameStep.js";
@@ -61,10 +60,8 @@ const Body = ({
61
60
  isDisconnecting
62
61
  }) => {
63
62
  const currentStep = useModalStore((state) => state.step);
64
- const onRampConfig = useModalStore((state) => state.onRampConfig);
65
63
  const stepDirection = useModalStore((state) => state.stepDirection);
66
64
  const setStepDirection = useModalStore((state) => state.setStepDirection);
67
- const accountAddFundTab = useModalStore((state) => state.accountAddFundTab);
68
65
  const setAccountAddFundTab = useModalStore((state) => state.setAccountAddFundTab);
69
66
  const modalError = useModalStore((state) => state.modalError);
70
67
  const setModalError = useModalStore((state) => state.setModalError);
@@ -72,8 +69,6 @@ const Body = ({
72
69
  var _a;
73
70
  return (_a = state.modalConfig) == null ? void 0 : _a.embeddedModal;
74
71
  });
75
- const appName = useStore((state) => state.appName);
76
- const [isTestModeAlert, setIsTestModeAlert] = useState(onRampConfig == null ? void 0 : onRampConfig.testMode);
77
72
  const Content = () => {
78
73
  switch (currentStep) {
79
74
  case ModalStep.AUTH_MAIN: {
@@ -231,11 +226,6 @@ const Body = ({
231
226
  }
232
227
  }
233
228
  };
234
- useEffect(() => {
235
- if (!isTestModeAlert && (onRampConfig == null ? void 0 : onRampConfig.testMode)) {
236
- setIsTestModeAlert(true);
237
- }
238
- }, [onRampConfig == null ? void 0 : onRampConfig.testMode]);
239
229
  useEffect(() => {
240
230
  switch (currentStep) {
241
231
  case ModalStep.ADD_FUNDS_BUY:
@@ -269,6 +259,7 @@ const Body = ({
269
259
  children: /* @__PURE__ */ jsxs(
270
260
  BodyContainer,
271
261
  {
262
+ "data-step": currentStep.toLowerCase().replace(/_/g, "-"),
272
263
  custom: stepDirection,
273
264
  variants: BODY_MOTION_VARIANTS,
274
265
  initial: "enter",
@@ -284,21 +275,7 @@ const Body = ({
284
275
  $isIFrameStep: IFrameSteps.includes(currentStep),
285
276
  children: [
286
277
  !IFrameSteps.includes(currentStep) && /* @__PURE__ */ jsx(NetworkSpeedBanner, { fontSize: "12px", iconSize: "16px" }),
287
- Content(),
288
- (onRampConfig == null ? void 0 : onRampConfig.testMode) && [
289
- ModalStep.ADD_FUNDS_BUY,
290
- ModalStep.ADD_FUNDS_WITHDRAW,
291
- ModalStep.ADD_FUNDS_AWAITING,
292
- ModalStep.ADD_FUNDS_FAILURE,
293
- ModalStep.ADD_FUNDS_SUCCESS
294
- ].includes(currentStep) && isTestModeAlert && accountAddFundTab !== EnabledFlow.RECEIVE && /* @__PURE__ */ jsx(TestModeAlert, { children: /* @__PURE__ */ jsxs("div", { style: { fontSize: "14px" }, children: [
295
- "This Para Modal is configured to run on-ramp services in ",
296
- /* @__PURE__ */ jsx("b", { children: "test mode" }),
297
- " only, for development purposes. If you are a user of ",
298
- appName,
299
- ", please contact support.",
300
- /* @__PURE__ */ jsx(CloseButton, { onClick: () => setIsTestModeAlert(false), children: /* @__PURE__ */ jsx(CloseX, { icon: "x" }) })
301
- ] }) })
278
+ Content()
302
279
  ]
303
280
  }
304
281
  ),
@@ -350,31 +327,6 @@ const InnerContainer = safeStyled.div`
350
327
  padding: ${({ $embeddedModal, $isIFrameStep }) => $isIFrameStep ? "0px" : $embeddedModal ? "12px 0px 0px" : "0px"};
351
328
  }
352
329
  `;
353
- const TestModeAlert = safeStyled(CpslAlert)`
354
- --container-padding-end: 40px;
355
- position: absolute;
356
- bottom: 16px;
357
- left: 16px;
358
- right: 16px;
359
- z-index: 1000;
360
- `;
361
- const CloseButton = safeStyled.button`
362
- background-color: transparent;
363
- border: none;
364
- padding: 0;
365
- cursor: pointer;
366
- flex-shrink: 0;
367
- display: flex;
368
- align-items: center;
369
- justify-content: center;
370
- width: 20px;
371
- height: 20px;
372
- `;
373
- const CloseX = safeStyled(CpslIcon)`
374
- --icon-color: var(--cpsl-color-utility-yellow-dark, #92400e);
375
- --height: 18px;
376
- --width: 18px;
377
- `;
378
330
  export {
379
331
  Body
380
332
  };
@@ -73,23 +73,39 @@ const ExternalWalletMobileConnect = ({
73
73
  secondaryText: externalWalletError == null ? void 0 : externalWalletError[1]
74
74
  }
75
75
  ),
76
- wallet.type === "SOLANA" && isSolanaWalletInAppBrowser(wallet.internalId) && !wallet.hasIosSafariExtension || wallet.type !== "SOLANA" ? /* @__PURE__ */ jsx(CpslButton, { onClick: handleRetryClick, fullWidth: true, children: isError ? "Retry" : "Connect Wallet" }) : /* @__PURE__ */ jsx(Text, { weight: "semiBold", children: wallet.hasIosSafariExtension ? `Please install and use the ${wallet.name} extension for iOS Safari.` : `Please navigate to ${appName} in the ${wallet.name} wallet.` }),
77
- !wallet.hasIosSafariExtension && /* @__PURE__ */ jsx(Link, { href: (_a = wallet.downloadUrl) != null ? _a : "", target: "_blank", children: /* @__PURE__ */ jsxs(ExternalButton, { variant: "secondary", children: [
76
+ wallet.type === "SOLANA" && isSolanaWalletInAppBrowser(wallet.internalId) && !wallet.hasIosSafariExtension || wallet.type !== "SOLANA" ? /* @__PURE__ */ jsx(
77
+ CpslButton,
78
+ {
79
+ onClick: handleRetryClick,
80
+ fullWidth: true,
81
+ "data-testid": isError ? "para-wallet-retry" : "para-wallet-connect",
82
+ children: isError ? "Retry" : "Connect Wallet"
83
+ }
84
+ ) : /* @__PURE__ */ jsx(Text, { weight: "semiBold", children: wallet.hasIosSafariExtension ? `Please install and use the ${wallet.name} extension for iOS Safari.` : `Please navigate to ${appName} in the ${wallet.name} wallet.` }),
85
+ !wallet.hasIosSafariExtension && /* @__PURE__ */ jsx(Link, { href: (_a = wallet.downloadUrl) != null ? _a : "", target: "_blank", children: /* @__PURE__ */ jsxs(ExternalButton, { variant: "secondary", "data-testid": "para-wallet-get", children: [
78
86
  `Get ${wallet.name}`,
79
87
  /* @__PURE__ */ jsx(ExternalIcon, { icon: "linkExternal" })
80
88
  ] }) })
81
89
  ] })
82
90
  ] });
83
91
  }
84
- const GetWalletButton = /* @__PURE__ */ jsxs(ExternalButton, { variant: "secondary", onClick: isWalletConnect ? () => onConnectWc(wallet) : void 0, children: [
85
- `${isWalletConnect ? "Open" : "Get"} ${wallet.name}`,
86
- /* @__PURE__ */ jsx(ExternalIcon, { icon: "linkExternal" })
87
- ] });
92
+ const GetWalletButton = /* @__PURE__ */ jsxs(
93
+ ExternalButton,
94
+ {
95
+ variant: "secondary",
96
+ onClick: isWalletConnect ? () => onConnectWc(wallet) : void 0,
97
+ "data-testid": "para-wallet-get",
98
+ children: [
99
+ `${isWalletConnect ? "Open" : "Get"} ${wallet.name}`,
100
+ /* @__PURE__ */ jsx(ExternalIcon, { icon: "linkExternal" })
101
+ ]
102
+ }
103
+ );
88
104
  return /* @__PURE__ */ jsxs(Fragment, { children: [
89
105
  /* @__PURE__ */ jsxs(InnerStepContainer, { children: [
90
106
  /* @__PURE__ */ jsx(CpslText, { weight: "semiBold", children: "Scan with your mobile device" }),
91
- /* @__PURE__ */ jsx(QRContainer, { children: !qrUri ? /* @__PURE__ */ jsx(CpslSpinner, { size: 100 }) : /* @__PURE__ */ jsx(CpslQrCode, { url: qrUri, imageSrc: wallet.iconUrl }) }),
92
- /* @__PURE__ */ jsxs(CpslButton, { size: "small", variant: "ghost", onClick: handleCopy, children: [
107
+ /* @__PURE__ */ jsx(QRContainer, { "data-testid": "para-wallet-qr", children: !qrUri ? /* @__PURE__ */ jsx(CpslSpinner, { size: 100 }) : /* @__PURE__ */ jsx(CpslQrCode, { url: qrUri, imageSrc: wallet.iconUrl }) }),
108
+ /* @__PURE__ */ jsxs(CpslButton, { size: "small", variant: "ghost", onClick: handleCopy, "data-testid": "para-wallet-copy-link", children: [
93
109
  /* @__PURE__ */ jsx(CpslIcon, { slot: "start", icon: isCopied ? "check" : "copy" }),
94
110
  isCopied ? "Copied" : "Copy Link"
95
111
  ] })
@@ -153,6 +169,7 @@ Please choose another wallet or continue on desktop.` }) });
153
169
  target: "_blank",
154
170
  variant: "secondary",
155
171
  onClick: handleTryAgainClick,
172
+ "data-testid": isInstalled ? "para-wallet-retry" : "para-wallet-get",
156
173
  children: [
157
174
  /* @__PURE__ */ jsx(CpslIcon, { fullWidth: true, slot: "start", icon: isInstalled ? "refresh" : "linkExternal" }),
158
175
  isInstalled ? "Try Again" : `Get ${wallet.name}`
@@ -56,7 +56,7 @@ const ExternalWallets = ({ isAddingWallets = false }) => {
56
56
  }
57
57
  }
58
58
  });
59
- return /* @__PURE__ */ jsxs(Container, { $maxHeight: showAll, children: [
59
+ return /* @__PURE__ */ jsxs(Container, { $maxHeight: showAll, "data-testid": "para-external-wallets", children: [
60
60
  showAll && /* @__PURE__ */ jsxs(Fragment, { children: [
61
61
  /* @__PURE__ */ jsx(SearchInputWrapper, { children: /* @__PURE__ */ jsx(
62
62
  SearchInput,
@@ -68,10 +68,11 @@ const ExternalWallets = ({ isAddingWallets = false }) => {
68
68
  }),
69
69
  value: search,
70
70
  style: { width: "100%" },
71
+ "data-testid": "para-wallet-search",
71
72
  children: /* @__PURE__ */ jsx(SearchIcon, { slot: "start", icon: "search" })
72
73
  }
73
74
  ) }),
74
- hasEmbeddedAuth(authLayout != null ? authLayout : []) && !isAddingWallets && /* @__PURE__ */ jsx(CpslButton, { fullWidth: true, variant: "tertiary", onClick: handleParaClick, children: /* @__PURE__ */ jsxs(WalletButtonOuterContainer, { children: [
75
+ hasEmbeddedAuth(authLayout != null ? authLayout : []) && !isAddingWallets && /* @__PURE__ */ jsx(CpslButton, { fullWidth: true, variant: "tertiary", onClick: handleParaClick, "data-testid": "para-wallet-para", children: /* @__PURE__ */ jsxs(WalletButtonOuterContainer, { children: [
75
76
  /* @__PURE__ */ jsxs(WalletButtonInnerContainer, { children: [
76
77
  /* @__PURE__ */ jsx(CpslIcon, { slot: "start", icon: "paraIcon" }),
77
78
  /* @__PURE__ */ jsx(CpslText, { weight: "medium", children: "Para" })
@@ -80,18 +81,37 @@ const ExternalWallets = ({ isAddingWallets = false }) => {
80
81
  ] }) })
81
82
  ] }),
82
83
  walletsToShow.map(
83
- (wallet) => showAll ? /* @__PURE__ */ jsx(CpslButton, { fullWidth: true, variant: "tertiary", onClick: handleWalletClick(wallet), children: /* @__PURE__ */ jsxs(WalletButtonOuterContainer, { children: [
84
- /* @__PURE__ */ jsxs(WalletButtonInnerContainer, { children: [
85
- /* @__PURE__ */ jsx(CpslIcon, { slot: "start", src: wallet.iconUrl }),
86
- /* @__PURE__ */ jsx(CpslText, { weight: "medium", children: wallet.name })
87
- ] }),
88
- /* @__PURE__ */ jsx(Badge, { $show: !!wallet.isMobile || !!wallet.installed, $variant: wallet.installed ? "installed" : "mobile", children: /* @__PURE__ */ jsx(CpslText, { variant: "body2XS", weight: "medium", children: wallet.installed ? "Installed" : "Mobile" }) })
89
- ] }) }, wallet.id) : /* @__PURE__ */ jsx(WalletTileButton, { src: wallet.iconUrl, onClick: handleWalletClick(wallet), children: /* @__PURE__ */ jsxs(TileButtonInnerContainer, { children: [
90
- wallet.installed && /* @__PURE__ */ jsx(InstalledIndicator, {}),
91
- /* @__PURE__ */ jsx(CpslText, { variant: "bodyXS", color: "secondary", weight: "medium", children: wallet.name })
92
- ] }) }, wallet.id)
84
+ (wallet) => showAll ? /* @__PURE__ */ jsx(
85
+ CpslButton,
86
+ {
87
+ fullWidth: true,
88
+ variant: "tertiary",
89
+ onClick: handleWalletClick(wallet),
90
+ "data-testid": `para-wallet-${wallet.id.replace(/[^a-z0-9]/gi, "-").toLowerCase()}`,
91
+ children: /* @__PURE__ */ jsxs(WalletButtonOuterContainer, { children: [
92
+ /* @__PURE__ */ jsxs(WalletButtonInnerContainer, { children: [
93
+ /* @__PURE__ */ jsx(CpslIcon, { slot: "start", src: wallet.iconUrl }),
94
+ /* @__PURE__ */ jsx(CpslText, { weight: "medium", children: wallet.name })
95
+ ] }),
96
+ /* @__PURE__ */ jsx(Badge, { $show: !!wallet.isMobile || !!wallet.installed, $variant: wallet.installed ? "installed" : "mobile", children: /* @__PURE__ */ jsx(CpslText, { variant: "body2XS", weight: "medium", children: wallet.installed ? "Installed" : "Mobile" }) })
97
+ ] })
98
+ },
99
+ wallet.id
100
+ ) : /* @__PURE__ */ jsx(
101
+ WalletTileButton,
102
+ {
103
+ src: wallet.iconUrl,
104
+ onClick: handleWalletClick(wallet),
105
+ "data-testid": `para-wallet-${wallet.id.replace(/[^a-z0-9]/gi, "-").toLowerCase()}`,
106
+ children: /* @__PURE__ */ jsxs(TileButtonInnerContainer, { children: [
107
+ wallet.installed && /* @__PURE__ */ jsx(InstalledIndicator, {}),
108
+ /* @__PURE__ */ jsx(CpslText, { variant: "bodyXS", color: "secondary", weight: "medium", children: wallet.name })
109
+ ] })
110
+ },
111
+ wallet.id
112
+ )
93
113
  ),
94
- showMoreButton && /* @__PURE__ */ jsxs(CpslButton, { variant: "tertiary", fullWidth: true, onClick: handleShowAll, children: [
114
+ showMoreButton && /* @__PURE__ */ jsxs(CpslButton, { variant: "tertiary", fullWidth: true, onClick: handleShowAll, "data-testid": "para-more-wallets", children: [
95
115
  /* @__PURE__ */ jsx(CpslIcon, { slot: "start", icon: "wallet" }),
96
116
  "More Wallets"
97
117
  ] }),
@@ -48,7 +48,7 @@ const IFrameStep = () => {
48
48
  };
49
49
  }, [setIsReady, iFrameUrl]);
50
50
  return /* @__PURE__ */ jsxs(OuterContainer, { $isVisible: IFrameSteps.includes(currentStep), $embeddedModal: !!embeddedModal, $isReady: !!isReady, children: [
51
- /* @__PURE__ */ jsx(Container, { $isReady: !!isReady, $height: height, children: /* @__PURE__ */ jsx("iframe", { src: iFrameUrl, ref: refs.iFrame }) }),
51
+ /* @__PURE__ */ jsx(Container, { $isReady: !!isReady, $height: height, children: /* @__PURE__ */ jsx("iframe", { src: iFrameUrl, ref: refs.iFrame, "data-testid": "para-portal-iframe" }) }),
52
52
  !isReady && /* @__PURE__ */ jsx(SpinnerContainer, { style: { width: "100%", height: "100%", flex: 1, position: "absolute" }, children: /* @__PURE__ */ jsx(CpslSpinner, { size: 100 }) })
53
53
  ] });
54
54
  };
@@ -38,7 +38,7 @@ const OAuth = ({ methods }) => {
38
38
  const useBrandedLogos = oAuthLogoVariant === "default";
39
39
  const useDarkLogos = useBrandedLogos ? isDark : oAuthLogoVariant !== "dark";
40
40
  const showMoreButton = !showAll && hasMore;
41
- return /* @__PURE__ */ jsxs(OAuthContainer, { children: [
41
+ return /* @__PURE__ */ jsxs(OAuthContainer, { "data-testid": "para-oauth-methods", children: [
42
42
  methodsToShow.map((method, index) => /* @__PURE__ */ jsx(
43
43
  OAuthButton,
44
44
  {
@@ -46,7 +46,8 @@ const OAuth = ({ methods }) => {
46
46
  icon: ACCOUNT_TYPES[method][useBrandedLogos ? "iconBranded" : "icon"],
47
47
  onClick: handleMethodClick(method),
48
48
  $index: index,
49
- $totalItems: showMoreButton ? HAS_MORE_LENGTH : methodsToShow.length
49
+ $totalItems: showMoreButton ? HAS_MORE_LENGTH : methodsToShow.length,
50
+ "data-testid": `para-oauth-${method.toLowerCase()}`
50
51
  },
51
52
  method
52
53
  )),
@@ -57,7 +58,8 @@ const OAuth = ({ methods }) => {
57
58
  icon: "moreLoginOptions",
58
59
  onClick: handleShowAll,
59
60
  $index: HAS_MORE_LENGTH - 1,
60
- $totalItems: HAS_MORE_LENGTH
61
+ $totalItems: HAS_MORE_LENGTH,
62
+ "data-testid": "para-oauth-more"
61
63
  }
62
64
  )
63
65
  ] });
@@ -3,44 +3,36 @@ import {
3
3
  __async
4
4
  } from "../../../chunk-MMUBH76A.js";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
- import { useState } from "react";
7
6
  import { safeStyled, ON_RAMP_PROVIDERS } from "@getpara/react-common";
8
- import { CpslButton, CpslIcon, CpslSpinner, CpslText } from "@getpara/react-components";
7
+ import { CpslButton, CpslIcon, CpslText } from "@getpara/react-components";
9
8
  import { motion } from "framer-motion";
10
9
  const OnRampProviderButton = ({ config, index, onClick: _onClick }) => {
11
- const [isLoading, setIsLoading] = useState(false);
12
10
  const provider = config.providers[index];
13
- const { feeLower, feeUpper, name, icon, backgroundColors } = ON_RAMP_PROVIDERS[provider];
14
- const feeString = `Fee ${feeLower}% - ${feeUpper}%`;
11
+ const { feeLower, name, methods, icon, backgroundColors } = ON_RAMP_PROVIDERS[provider];
12
+ const feeString = `Fee: ${feeLower}%`;
15
13
  const onClick = () => __async(void 0, null, function* () {
16
- setIsLoading(true);
17
14
  yield _onClick();
18
- setIsLoading(false);
19
15
  });
20
- return /* @__PURE__ */ jsx(StyledButton, { $gradientColors: backgroundColors, fullWidth: true, onClick, children: /* @__PURE__ */ jsxs(Container, { $backgroundColor: backgroundColors[1], children: [
21
- /* @__PURE__ */ jsx(IconContainer, { children: /* @__PURE__ */ jsx(CpslIcon, { icon }) }),
16
+ return /* @__PURE__ */ jsx(StyledButton, { variant: "tertiary", fullWidth: true, onClick, children: /* @__PURE__ */ jsxs(Container, { children: [
17
+ /* @__PURE__ */ jsx(IconContainer, { children: /* @__PURE__ */ jsx(CpslIcon, { icon, color: backgroundColors[0] }) }),
22
18
  /* @__PURE__ */ jsxs(ProviderInfoContainer, { children: [
23
19
  /* @__PURE__ */ jsx(Text, { variant: "bodyL", weight: "medium", children: name }),
24
- /* @__PURE__ */ jsx(ProviderInfoInnerContainer, { children: /* @__PURE__ */ jsx(Text, { variant: "bodyXS", weight: "medium", children: feeString }) })
25
- ] }),
26
- isLoading ? /* @__PURE__ */ jsx(CpslSpinner, { size: 16 }) : /* @__PURE__ */ jsx(Chevron, { icon: "chevronUp" })
20
+ /* @__PURE__ */ jsxs(ProviderInfoInnerContainer, { children: [
21
+ /* @__PURE__ */ jsx(Text, { variant: "bodyXS", weight: "medium", children: methods.join(", ") }),
22
+ /* @__PURE__ */ jsx(Text, { variant: "bodyXS", weight: "medium", children: feeString })
23
+ ] })
24
+ ] })
27
25
  ] }) });
28
26
  };
29
27
  const StyledButton = safeStyled(CpslButton)`
30
28
  width: 100%;
31
- --button-primary-background-color: ${({ $gradientColors }) => `linear-gradient(90deg, ${$gradientColors[0]} 0%, ${$gradientColors[1]} 100%)`};
32
- --button-primary-hover-background-color: ${({ $gradientColors }) => `linear-gradient(90deg, ${$gradientColors[0]} 0%, ${$gradientColors[0]} 100%)`};
33
- --button-primary-active-background-color: ${({ $gradientColors }) => `linear-gradient(90deg, ${$gradientColors[0]} 0%, ${$gradientColors[0]} 100%)`};
34
29
  `;
35
30
  const Container = safeStyled(motion.div)`
36
31
  display: flex;
37
32
  gap: 8px;
38
33
  flex: 1;
39
34
  align-items: center;
40
-
41
- & cpsl-spinner {
42
- --background-color: ${({ $backgroundColor }) => `${$backgroundColor}`};
43
- }
35
+ width: 100%;
44
36
  `;
45
37
  const ProviderInfoContainer = safeStyled.div`
46
38
  flex: 1;
@@ -50,27 +42,26 @@ const ProviderInfoContainer = safeStyled.div`
50
42
  gap: 2px;
51
43
  `;
52
44
  const ProviderInfoInnerContainer = safeStyled.div`
45
+ width: 100%;
53
46
  display: flex;
47
+ justify-content: space-between;
54
48
  gap: 16px;
55
49
  `;
56
50
  const IconContainer = safeStyled.span`
57
51
  display: flex;
58
52
  align-items: center;
59
53
  justify-content: center;
60
- background-color: #fff;
54
+ background-color: var(--cpsl-color-background-0);
61
55
  border-radius: 100%;
62
56
  height: 48px;
63
57
  width: 48px;
58
+ flex-shrink: 0;
64
59
  `;
65
60
  const Text = safeStyled(CpslText)`
66
61
  &::part(text-element) {
67
62
  color: #fff;
68
63
  }
69
64
  `;
70
- const Chevron = safeStyled(CpslIcon)`
71
- transform: rotate(90deg);
72
- --icon-color: #fff;
73
- `;
74
65
  export {
75
66
  OnRampProviderButton
76
67
  };
@@ -2,21 +2,46 @@
2
2
  import "../../chunk-MMUBH76A.js";
3
3
  import { jsx, jsxs } from "react/jsx-runtime";
4
4
  import { safeStyled } from "@getpara/react-common";
5
+ import { useEffect, useRef, useState } from "react";
5
6
  function QuantityInput({
6
7
  value,
7
8
  onChange,
8
9
  onFocus,
9
10
  onBlur,
10
11
  placeholder,
11
- size = "72px",
12
+ size = "56px",
12
13
  symbol
13
14
  }) {
15
+ const measureRef = useRef(null);
16
+ const minWidthRef = useRef(null);
17
+ const displayValue = value != null ? value : "";
18
+ const measureText = displayValue || placeholder || "0";
19
+ const placeholderText = placeholder || "0";
20
+ const fontSize = parseFloat(size) || 56;
21
+ const [inputWidth, setInputWidth] = useState(
22
+ () => Math.max(measureText.length, placeholderText.length) * fontSize * 0.5 + 16
23
+ );
24
+ useEffect(() => {
25
+ if (measureRef.current && minWidthRef.current) {
26
+ const textWidth = measureRef.current.getBoundingClientRect().width;
27
+ const minWidth = minWidthRef.current.getBoundingClientRect().width;
28
+ if (textWidth === 0 && minWidth === 0) return;
29
+ const buffer = 16;
30
+ const width = Math.max(textWidth, minWidth) + buffer;
31
+ setInputWidth(width);
32
+ }
33
+ }, [displayValue, placeholderText]);
14
34
  return /* @__PURE__ */ jsxs(Container, { style: { fontSize: size, position: "relative" }, children: [
15
- symbol && /* @__PURE__ */ jsx(CurrencySign, { slot: "start", children: symbol }),
35
+ /* @__PURE__ */ jsx(MeasureSpan, { ref: measureRef, "aria-hidden": "true", children: measureText }),
36
+ /* @__PURE__ */ jsx(MeasureSpan, { ref: minWidthRef, "aria-hidden": "true", children: placeholderText }),
37
+ symbol && /* @__PURE__ */ jsx(CurrencySign, { children: symbol }),
16
38
  /* @__PURE__ */ jsx(
17
39
  Input,
18
40
  {
19
- value: value != null ? value : "",
41
+ type: "number",
42
+ inputMode: "decimal",
43
+ value: displayValue,
44
+ style: { width: `${inputWidth}px` },
20
45
  onFocus: (e) => {
21
46
  e.currentTarget.select();
22
47
  onFocus == null ? void 0 : onFocus();
@@ -32,13 +57,19 @@ function QuantityInput({
32
57
  onChange(null);
33
58
  return;
34
59
  }
35
- const numericValue = rawValue.replace(/[^0-9.]/g, "");
60
+ let numericValue = rawValue.replace(/[^0-9.]/g, "");
36
61
  if (numericValue === "") {
37
62
  onChange(null);
38
63
  return;
39
64
  }
40
65
  const isValidNumberFormat = /^\d*\.?\d*$/.test(numericValue);
41
66
  if (isValidNumberFormat) {
67
+ if (symbol === "$") {
68
+ const parts = numericValue.split(".");
69
+ if (parts.length > 1 && parts[1].length > 2) {
70
+ numericValue = `${parts[0]}.${parts[1].slice(0, 2)}`;
71
+ }
72
+ }
42
73
  onChange(numericValue);
43
74
  } else {
44
75
  onChange(null);
@@ -51,7 +82,12 @@ function QuantityInput({
51
82
  } else {
52
83
  const parsed = parseFloat(numericValue);
53
84
  if (!isNaN(parsed)) {
54
- onChange(parsed.toString());
85
+ if (symbol === "$") {
86
+ const clamped = Math.round(parsed * 100) / 100;
87
+ onChange(clamped.toFixed(2));
88
+ } else {
89
+ onChange(parsed.toString());
90
+ }
55
91
  } else {
56
92
  onChange(null);
57
93
  }
@@ -70,9 +106,17 @@ const Container = safeStyled.div`
70
106
  font-family: var(--cpsl-font-family);
71
107
  color: var(--cpsl-color-text-primary);
72
108
  `;
109
+ const MeasureSpan = safeStyled.span`
110
+ position: absolute;
111
+ visibility: hidden;
112
+ height: 0;
113
+ overflow: hidden;
114
+ white-space: pre;
115
+ font-family: inherit;
116
+ font-size: inherit;
117
+ `;
73
118
  const CurrencySign = safeStyled.div`
74
- position: relative;
75
- left: 0px;
119
+ flex-shrink: 0;
76
120
  `;
77
121
  const Input = safeStyled.input`
78
122
  font-family: var(--cpsl-font-family);
@@ -82,9 +126,18 @@ const Input = safeStyled.input`
82
126
  height: auto;
83
127
  border-width: 0;
84
128
  text-align: center;
85
- width: 100%;
86
129
  outline: none;
87
- field-sizing: content;
130
+ min-width: 1ch;
131
+ padding: 0;
132
+ margin: 0;
133
+
134
+ /* Hide number input spinners */
135
+ -moz-appearance: textfield;
136
+ &::-webkit-outer-spin-button,
137
+ &::-webkit-inner-spin-button {
138
+ -webkit-appearance: none;
139
+ margin: 0;
140
+ }
88
141
  `;
89
142
  export {
90
143
  QuantityInput
@@ -41,12 +41,12 @@ const SaveRecoverySecret = ({
41
41
  /* @__PURE__ */ jsxs(InnerStepContainer, { children: [
42
42
  /* @__PURE__ */ jsx(Heading, { children: "Save your Recovery Secret" }),
43
43
  /* @__PURE__ */ jsxs(ButtonContainer, { children: [
44
- /* @__PURE__ */ jsx(ActionButton, { icon: "download", onClick: onDownload, children: /* @__PURE__ */ jsx(CpslText, { variant: "bodyXS", color: "secondary", weight: "medium", children: "Download" }) }),
45
- /* @__PURE__ */ jsx(ActionButton, { icon: isCopied ? "check" : "copy", onClick: onCopy, children: /* @__PURE__ */ jsx(CpslText, { variant: "bodyXS", color: "secondary", weight: "medium", children: isCopied ? "Copied!" : "Copy" }) }),
46
- /* @__PURE__ */ jsx(ActionButton, { icon: "send", onClick: onEmail, children: /* @__PURE__ */ jsx(CpslText, { variant: "bodyXS", color: "secondary", weight: "medium", children: "Email" }) })
44
+ /* @__PURE__ */ jsx(ActionButton, { icon: "download", onClick: onDownload, "data-testid": "para-recovery-download", children: /* @__PURE__ */ jsx(CpslText, { variant: "bodyXS", color: "secondary", weight: "medium", children: "Download" }) }),
45
+ /* @__PURE__ */ jsx(ActionButton, { icon: isCopied ? "check" : "copy", onClick: onCopy, "data-testid": "para-recovery-copy", children: /* @__PURE__ */ jsx(CpslText, { variant: "bodyXS", color: "secondary", weight: "medium", children: isCopied ? "Copied!" : "Copy" }) }),
46
+ /* @__PURE__ */ jsx(ActionButton, { icon: "send", onClick: onEmail, "data-testid": "para-recovery-email", children: /* @__PURE__ */ jsx(CpslText, { variant: "bodyXS", color: "secondary", weight: "medium", children: "Email" }) })
47
47
  ] })
48
48
  ] }),
49
- /* @__PURE__ */ jsx(InnerStepContainer, { children: /* @__PURE__ */ jsx(CpslButton, { fullWidth: true, onClick: onComplete, disabled: !isSecretSaved, children: !isSecretSaved ? "Choose an option above to continue" : "I\u2019ve saved my recovery secret" }) })
49
+ /* @__PURE__ */ jsx(InnerStepContainer, { children: /* @__PURE__ */ jsx(CpslButton, { fullWidth: true, onClick: onComplete, disabled: !isSecretSaved, "data-testid": "para-recovery-confirm", children: !isSecretSaved ? "Choose an option above to continue" : "I\u2019ve saved my recovery secret" }) })
50
50
  ] });
51
51
  };
52
52
  const RecoverySecretStep = () => {
@@ -21,7 +21,8 @@ export declare const contentMotionProps: {
21
21
  opacity: number;
22
22
  };
23
23
  };
24
- export declare function SearchableButtonList<T>({ items, transformItem, searchFilter, searchPlaceholder, onSelect, }: {
24
+ export declare function SearchableButtonList<T>({ height, items, transformItem, searchFilter, searchPlaceholder, onSelect, }: {
25
+ height?: number;
25
26
  items: T[];
26
27
  transformItem: (_: T) => AssetItem;
27
28
  searchFilter?: (_: {
@@ -18,6 +18,7 @@ const contentMotionProps = {
18
18
  const ESTIMATED_ITEM_HEIGHT = 72;
19
19
  const VISIBILITY_BUFFER = 3;
20
20
  function SearchableButtonList({
21
+ height,
21
22
  items,
22
23
  transformItem,
23
24
  searchFilter,
@@ -105,7 +106,7 @@ function SearchableButtonList({
105
106
  }, []);
106
107
  const paddingTop = visibleRange.start * ESTIMATED_ITEM_HEIGHT;
107
108
  const paddingBottom = Math.max(0, (transformedAndFiltered.length - visibleRange.end) * ESTIMATED_ITEM_HEIGHT);
108
- const MAX_CONTAINER_HEIGHT = 480;
109
+ const MAX_CONTAINER_HEIGHT = height || 480;
109
110
  useEffect(() => {
110
111
  var _a, _b;
111
112
  const container = (_b = (_a = scrollContainerRef.current) == null ? void 0 : _a.parentElement) == null ? void 0 : _b.parentElement;
@@ -132,6 +133,7 @@ function SearchableButtonList({
132
133
  ref: outerContainerRef,
133
134
  style: {
134
135
  display: "flex",
136
+ width: "100%",
135
137
  flexDirection: "column",
136
138
  height: `${outerContainerHeight}px`,
137
139
  maxHeight: `${MAX_CONTAINER_HEIGHT}px`,
@@ -105,20 +105,21 @@ const Setup2FAStep = ({ onClose }) => {
105
105
  length: 6,
106
106
  onKeyDown: (e) => __async(void 0, null, function* () {
107
107
  return e.key === "Enter" && (yield handleSubmitCode());
108
- })
108
+ }),
109
+ "data-testid": "para-2fa-code"
109
110
  }
110
111
  )
111
112
  }
112
113
  ) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
113
114
  /* @__PURE__ */ jsx(CpslText, { variant: "bodyS", color: "secondary", weight: "medium", children: "Scan with your preferred authenticator app" }),
114
- /* @__PURE__ */ jsx(QRContainer, { children: !(twoFactorStatus == null ? void 0 : twoFactorStatus.uri) ? /* @__PURE__ */ jsx(CpslSpinner, { size: 100 }) : /* @__PURE__ */ jsx(CpslQrCode, { url: twoFactorStatus.uri }) })
115
+ /* @__PURE__ */ jsx(QRContainer, { children: !(twoFactorStatus == null ? void 0 : twoFactorStatus.uri) ? /* @__PURE__ */ jsx(CpslSpinner, { size: 100 }) : /* @__PURE__ */ jsx(CpslQrCode, { url: twoFactorStatus.uri, "data-testid": "para-2fa-qr" }) })
115
116
  ] }) }),
116
117
  !isVerifying && /* @__PURE__ */ jsxs(Fragment, { children: [
117
118
  /* @__PURE__ */ jsx(InnerStepContainer, { children: /* @__PURE__ */ jsx(CpslDivider, { children: "or enter the code manually" }) }),
118
- /* @__PURE__ */ jsx(InnerStepContainer, { children: /* @__PURE__ */ jsx(FilledDisabledInput, { disabled: true, value: secret != null ? secret : "", noAutoDisable: true, children: /* @__PURE__ */ jsx(CpslButton, { slot: "end", variant: "ghost", onClick: handleCopy, children: /* @__PURE__ */ jsx(CpslIcon, { icon: copied ? "check" : "copy" }) }) }) }),
119
+ /* @__PURE__ */ jsx(InnerStepContainer, { children: /* @__PURE__ */ jsx(FilledDisabledInput, { disabled: true, value: secret != null ? secret : "", noAutoDisable: true, "data-testid": "para-2fa-secret", children: /* @__PURE__ */ jsx(CpslButton, { slot: "end", variant: "ghost", onClick: handleCopy, "data-testid": "para-2fa-copy", children: /* @__PURE__ */ jsx(CpslIcon, { icon: copied ? "check" : "copy" }) }) }) }),
119
120
  /* @__PURE__ */ jsxs(InnerStepContainer, { children: [
120
- /* @__PURE__ */ jsx(CpslButton, { fullWidth: true, onClick: handleNext, children: "Continue" }),
121
- /* @__PURE__ */ jsx(SkipButton, { variant: "ghost", onClick: handleSkip, children: "Skip" })
121
+ /* @__PURE__ */ jsx(CpslButton, { fullWidth: true, onClick: handleNext, "data-testid": "para-2fa-continue", children: "Continue" }),
122
+ /* @__PURE__ */ jsx(SkipButton, { variant: "ghost", onClick: handleSkip, "data-testid": "para-2fa-skip", children: "Skip" })
122
123
  ] })
123
124
  ] })
124
125
  ] });