@stokr/components-library 3.0.31 → 3.0.33

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/README.md +72 -287
  2. package/dist/analytics/index.js +2 -1
  3. package/dist/components/2FA/Connect2FA.js +11 -1
  4. package/dist/components/2FA/EnterCode.js +13 -2
  5. package/dist/components/2FA/InstallAuthApp.js +4 -0
  6. package/dist/components/2FA/ResetCode.js +2 -1
  7. package/dist/components/2FA/Sucess2FA.js +14 -2
  8. package/dist/components/2FA/disable-2fa-flow.js +15 -2
  9. package/dist/components/2FA/enable-2fa-flow.js +5 -0
  10. package/dist/components/2FA/login-with-otp-flow.js +2 -2
  11. package/dist/components/2FA/main-flow.js +3 -0
  12. package/dist/components/Button/GlareButton.js +273 -0
  13. package/dist/components/ConfirmModal/ConfirmModal.js +21 -2
  14. package/dist/components/FAQ/FAQ.js +37 -23
  15. package/dist/components/ForgotPasswordModal/ForgotPasswordModal.js +24 -3
  16. package/dist/components/Header/Header.js +1 -1
  17. package/dist/components/Input/InputPassword.js +27 -12
  18. package/dist/components/Input/OtpInput.js +21 -7
  19. package/dist/components/Input/Select.js +11 -2
  20. package/dist/components/Input/TableFilterDropdown.js +23 -7
  21. package/dist/components/Layout/Layout.js +6 -3
  22. package/dist/components/LoginModal/LoginModal.js +7 -4
  23. package/dist/components/Modal/Modal.styles.js +10 -2
  24. package/dist/components/Modal/NewVentureModal/NewVentureModal.js +21 -6
  25. package/dist/components/Modal/PaymentModal.js +16 -16
  26. package/dist/components/RegisterConfirmModal/RegisterConfirmModal.js +1 -1
  27. package/dist/components/RegisterModal/RegisterModal.js +30 -5
  28. package/dist/components/ResetConfirmModal/ResetConfirmModal.js +1 -1
  29. package/dist/components/ResetPasswordModal/ResetPasswordModal.js +16 -4
  30. package/dist/components/StepController/StepControllerProgress.js +1 -0
  31. package/dist/components/Switch/Switch.js +5 -3
  32. package/dist/components/ToDoList/ToDoListTask.js +1 -0
  33. package/dist/components/VerifyEmailModal/VerifyEmailModal.js +14 -2
  34. package/dist/components/headerHo/HeaderHo.js +2 -0
  35. package/dist/components/taxId/complete.js +23 -2
  36. package/dist/components/taxId/flow.js +11 -1
  37. package/dist/components/taxId/register-taxid.js +25 -4
  38. package/dist/index.js +2 -0
  39. package/dist/utils/formatCurrencyValue.js +4 -2
  40. package/package.json +1 -1
@@ -11,7 +11,7 @@ import { authenticationApi } from "../../api/authenticationApi.js";
11
11
  import { getConfig, getPlatformURL } from "../../runtime-config.js";
12
12
  import { navigateToHref } from "../../routing/navigate-app.js";
13
13
  import { useNavigate } from "react-router-dom";
14
- const LoginWithOTP = ({ withBackground }) => {
14
+ const LoginWithOTP = ({ withBackground, useRelativePathForMenu = false }) => {
15
15
  const navigate = useNavigate();
16
16
  const { loginUser, waitingFor2fa, firebaseError, loginUserWithTotp, reset2faFlow } = useContext(AuthContext);
17
17
  const [isModalOpen, setIsModalOpen] = useState({
@@ -60,7 +60,7 @@ const LoginWithOTP = ({ withBackground }) => {
60
60
  });
61
61
  };
62
62
  const backgroundProp = `url(${background}) left top no-repeat`;
63
- return /* @__PURE__ */ jsxs(Layout, { noHeader: true, noFooter: true, children: [
63
+ return /* @__PURE__ */ jsxs(Layout, { noHeader: true, noFooter: true, useRelativePathForMenu, children: [
64
64
  /* @__PURE__ */ jsx(
65
65
  stdin_default$1,
66
66
  {
@@ -129,6 +129,7 @@ const Main2FAFlow = ({
129
129
  children: /* @__PURE__ */ jsx(
130
130
  stdin_default$1,
131
131
  {
132
+ dataCy: "2fa-settings-toggle",
132
133
  value1: "disabled",
133
134
  value2: "enabled",
134
135
  value1Color: colors.orangishRed,
@@ -194,6 +195,8 @@ const Main2FAFlow = ({
194
195
  isFlowopen.requiresRecentLogin && /* @__PURE__ */ jsx(Modal, { isOpen: isFlowopen.requiresRecentLogin, onClose: () => handleFlowClose("requiresRecentLogin"), children: /* @__PURE__ */ jsx(ModalInner, { noPadding: true, children: /* @__PURE__ */ jsx(
195
196
  stdin_default$4,
196
197
  {
198
+ continueButtonId: "2fa-requires-recent-login-btn",
199
+ continueButtonDataCy: "2fa-requires-recent-login-submit",
197
200
  onClick: () => {
198
201
  onLoginAgainClick?.();
199
202
  },
@@ -0,0 +1,273 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import PropTypes from "prop-types";
3
+ import { Button } from "./Button.styles.js";
4
+ import { colors } from "../../styles/colors.js";
5
+ import { useInView } from "react-intersection-observer";
6
+ import { useState, useRef, useId, useEffect } from "react";
7
+ import styled, { keyframes, css } from "styled-components";
8
+ const config = {
9
+ colorDark: "rgb(41, 112, 225)",
10
+ colorLight: "#9bfcfd",
11
+ colorFooter: colors.blue,
12
+ strokeWidth: 2,
13
+ padding: 5,
14
+ baseHeight: 50
15
+ };
16
+ const glareAnimation = keyframes`
17
+ 0% { width: 0; }
18
+ 50% { width: 200%; }
19
+ 75% { opacity: 1; }
20
+ 100% { width: 400%; opacity: 0; }
21
+ `;
22
+ const ButtonWrapper = styled.div.withConfig({
23
+ shouldForwardProp: (props) => !["nowrap", "fullWidth"].includes(props)
24
+ })`
25
+ position: relative;
26
+ display: inline-block;
27
+
28
+ ${({ nowrap }) => !nowrap && css`
29
+ display: block;
30
+ position: absolute;
31
+ top: 0;
32
+ left: 50%;
33
+ transform: translate3d(-50%, -50%, 0);
34
+ `}
35
+
36
+ ${({ fullWidth }) => fullWidth && css`
37
+ width: 100%;
38
+ `}
39
+ `;
40
+ const StyledButton = styled(Button).withConfig({
41
+ shouldForwardProp: (props) => !["beauty", "backgroundColor", "textColor", "borderColor", "fullWidth", "disabledColor"].includes(props)
42
+ })`
43
+ position: relative;
44
+ margin: ${config.padding}px;
45
+ background-color: ${(props) => props.backgroundColor || "inherit"};
46
+ color: ${(props) => props.textColor || "inherit"};
47
+ border-color: ${(props) => props.borderColor || "inherit"};
48
+
49
+ ${({ fullWidth }) => fullWidth && css`
50
+ width: calc(100% - ${config.padding * 2}px);
51
+ `}
52
+
53
+ &:disabled {
54
+ background-color: ${(props) => props.disabledColor || "#333"} !important;
55
+ color: rgba(255, 255, 255, 0.5) !important;
56
+ border-color: #555 !important;
57
+ }
58
+ `;
59
+ const ButtonGlare = styled.svg`
60
+ position: absolute;
61
+ left: 0;
62
+ top: 0;
63
+ width: 100%;
64
+ height: 100%;
65
+ `;
66
+ const GlareShape = styled.rect.withConfig({
67
+ shouldForwardProp: (props) => !["animationDuration"].includes(props)
68
+ })`
69
+ animation: ${glareAnimation} ${(props) => props.animationDuration || 4}s linear infinite;
70
+ `;
71
+ const GlareButton = ({
72
+ children,
73
+ nowrap = false,
74
+ onClick,
75
+ disabled = false,
76
+ fullWidth = false,
77
+ disabledColor = "#333",
78
+ buttonId = "glare-button",
79
+ className,
80
+ backgroundColor,
81
+ textColor,
82
+ borderColor,
83
+ glareColorDark = config.colorDark,
84
+ glareColorLight = config.colorLight,
85
+ animationDuration = 4,
86
+ ...props
87
+ }) => {
88
+ const [isButtonVisible, setIsButtonVisible] = useState(false);
89
+ const [dimensions, setDimensions] = useState({
90
+ width: 295,
91
+ height: config.baseHeight
92
+ });
93
+ const buttonRef = useRef(null);
94
+ const wrapperRef = useRef(null);
95
+ const observerRef = useRef(null);
96
+ const mediaQueryRef = useRef(null);
97
+ const ids = useRef({
98
+ linearGradient: `gradient-${useId()}`,
99
+ fadeGradient: `fade-gradient-${useId()}`,
100
+ staticMask: `static-mask-${useId()}`,
101
+ mask: `mask-${useId()}`,
102
+ fadeMask: `fade-mask-${useId()}`
103
+ });
104
+ const updateDimensions = () => {
105
+ if (!buttonRef.current || !wrapperRef.current) return;
106
+ const buttonRect = buttonRef.current.getBoundingClientRect();
107
+ setDimensions({
108
+ width: buttonRect.width + config.padding * 2,
109
+ height: buttonRect.height + config.padding * 2
110
+ });
111
+ };
112
+ const handleButtonVisibility = (isVisible) => {
113
+ setIsButtonVisible(isVisible);
114
+ };
115
+ const { ref: windowViewportRef } = useInView({
116
+ threshold: 0,
117
+ // partialVisibility equivalent - triggers as soon as any part is visible
118
+ onChange: (inView, _entry) => {
119
+ handleButtonVisibility(inView);
120
+ }
121
+ });
122
+ useEffect(() => {
123
+ let mediaTimeoutId;
124
+ mediaQueryRef.current = window.matchMedia("(max-width: 768px)");
125
+ const handleMediaChange = (_event) => {
126
+ if (mediaTimeoutId != null) clearTimeout(mediaTimeoutId);
127
+ mediaTimeoutId = setTimeout(updateDimensions, 400);
128
+ };
129
+ mediaQueryRef.current.addEventListener("change", handleMediaChange);
130
+ observerRef.current = new ResizeObserver(updateDimensions);
131
+ if (buttonRef.current) {
132
+ observerRef.current.observe(buttonRef.current);
133
+ updateDimensions();
134
+ }
135
+ return () => {
136
+ if (mediaTimeoutId != null) clearTimeout(mediaTimeoutId);
137
+ mediaQueryRef.current.removeEventListener("change", handleMediaChange);
138
+ if (observerRef.current) {
139
+ observerRef.current.disconnect();
140
+ }
141
+ };
142
+ }, []);
143
+ return /* @__PURE__ */ jsx("div", { ref: windowViewportRef, children: /* @__PURE__ */ jsxs(ButtonWrapper, { ref: wrapperRef, nowrap, fullWidth, className, children: [
144
+ /* @__PURE__ */ jsxs(
145
+ ButtonGlare,
146
+ {
147
+ xmlns: "http://www.w3.org/2000/svg",
148
+ viewBox: `0 0 ${dimensions.width} ${dimensions.height}`,
149
+ preserveAspectRatio: "none",
150
+ children: [
151
+ /* @__PURE__ */ jsxs("defs", { children: [
152
+ /* @__PURE__ */ jsxs("filter", { id: "glow", x: "-50%", y: "-50%", width: "200%", height: "200%", children: [
153
+ /* @__PURE__ */ jsx("feGaussianBlur", { stdDeviation: "100", result: "coloredBlur" }),
154
+ /* @__PURE__ */ jsx("feMerge", { children: /* @__PURE__ */ jsx("feMergeNode", { in: "coloredBlur" }) })
155
+ ] }),
156
+ /* @__PURE__ */ jsxs("linearGradient", { id: `${ids.current.linearGradient}`, x1: "0%", y1: "50%", x2: "100%", y2: "50%", children: [
157
+ /* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: glareColorDark, stopOpacity: "0" }),
158
+ /* @__PURE__ */ jsx("stop", { offset: "20%", stopColor: glareColorDark, stopOpacity: "1" }),
159
+ /* @__PURE__ */ jsx("stop", { offset: "40%", stopColor: glareColorDark }),
160
+ /* @__PURE__ */ jsx("stop", { offset: "60%", stopColor: glareColorLight }),
161
+ /* @__PURE__ */ jsx("stop", { offset: "80%", stopColor: glareColorDark }),
162
+ /* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: glareColorDark, stopOpacity: "0" })
163
+ ] }),
164
+ /* @__PURE__ */ jsxs("linearGradient", { id: `${ids.current.fadeGradient}`, x1: "0%", y1: "25%", x2: "0%", y2: "100%", children: [
165
+ /* @__PURE__ */ jsx("stop", { offset: "0", stopColor: "white", stopOpacity: "1" }),
166
+ /* @__PURE__ */ jsx("stop", { offset: "1", stopColor: "white", stopOpacity: "0" })
167
+ ] }),
168
+ /* @__PURE__ */ jsx("mask", { id: `${ids.current.staticMask}`, children: /* @__PURE__ */ jsx(
169
+ "rect",
170
+ {
171
+ x: config.strokeWidth,
172
+ y: config.strokeWidth,
173
+ width: dimensions.width - config.strokeWidth * 2,
174
+ height: dimensions.height - config.strokeWidth * 2,
175
+ rx: (dimensions.height - config.strokeWidth * 2) / 2,
176
+ ry: (dimensions.height - config.strokeWidth * 2) / 2,
177
+ stroke: "#FFFFFF",
178
+ strokeWidth: config.strokeWidth + 1,
179
+ fill: "none"
180
+ }
181
+ ) }),
182
+ /* @__PURE__ */ jsx("mask", { id: `${ids.current.mask}`, children: /* @__PURE__ */ jsx(
183
+ "rect",
184
+ {
185
+ x: config.strokeWidth,
186
+ y: config.strokeWidth,
187
+ width: dimensions.width - config.strokeWidth * 2,
188
+ height: dimensions.height - config.strokeWidth * 2,
189
+ rx: (dimensions.height - config.strokeWidth * 2) / 2,
190
+ ry: (dimensions.height - config.strokeWidth * 2) / 2,
191
+ stroke: "#FFFFFF",
192
+ strokeWidth: config.strokeWidth + 1
193
+ }
194
+ ) }),
195
+ /* @__PURE__ */ jsx("mask", { id: `${ids.current.fadeMask}`, children: /* @__PURE__ */ jsx(
196
+ "rect",
197
+ {
198
+ x: config.strokeWidth,
199
+ y: config.strokeWidth,
200
+ width: dimensions.width - config.strokeWidth * 2,
201
+ height: dimensions.height - config.strokeWidth * 2,
202
+ rx: (dimensions.height - config.strokeWidth * 2) / 2,
203
+ ry: (dimensions.height - config.strokeWidth * 2) / 2,
204
+ fill: `url(#${ids.current.fadeGradient})`
205
+ }
206
+ ) })
207
+ ] }),
208
+ /* @__PURE__ */ jsx(
209
+ "rect",
210
+ {
211
+ x: "0",
212
+ y: "0",
213
+ width: dimensions.width,
214
+ height: dimensions.height,
215
+ fill: glareColorDark,
216
+ mask: `url(#${ids.current.staticMask})`,
217
+ filter: "url(#glow)"
218
+ }
219
+ ),
220
+ isButtonVisible && /* @__PURE__ */ jsx("g", { mask: `url(#${ids.current.fadeMask})`, children: /* @__PURE__ */ jsx(
221
+ GlareShape,
222
+ {
223
+ x: dimensions.width * -0.5,
224
+ y: config.strokeWidth,
225
+ width: dimensions.width * 2.5,
226
+ height: dimensions.height - config.strokeWidth * 2,
227
+ fill: `url(#${ids.current.linearGradient})`,
228
+ mask: `url(#${ids.current.mask})`,
229
+ animationDuration
230
+ }
231
+ ) })
232
+ ]
233
+ }
234
+ ),
235
+ /* @__PURE__ */ jsx(
236
+ StyledButton,
237
+ {
238
+ beauty: true,
239
+ disabled,
240
+ id: buttonId,
241
+ onClick,
242
+ fullWidth,
243
+ disabledColor,
244
+ backgroundColor,
245
+ textColor,
246
+ borderColor,
247
+ ref: buttonRef,
248
+ ...props,
249
+ "data-cy": props["data-cy"] ?? buttonId,
250
+ children
251
+ }
252
+ )
253
+ ] }) });
254
+ };
255
+ GlareButton.propTypes = {
256
+ children: PropTypes.node.isRequired,
257
+ nowrap: PropTypes.bool,
258
+ onClick: PropTypes.func.isRequired,
259
+ disabled: PropTypes.bool,
260
+ fullWidth: PropTypes.bool,
261
+ disabledColor: PropTypes.string,
262
+ buttonId: PropTypes.string,
263
+ className: PropTypes.string,
264
+ backgroundColor: PropTypes.string,
265
+ textColor: PropTypes.string,
266
+ borderColor: PropTypes.string,
267
+ glareColorDark: PropTypes.string,
268
+ glareColorLight: PropTypes.string,
269
+ animationDuration: PropTypes.number
270
+ };
271
+ export {
272
+ GlareButton
273
+ };
@@ -89,8 +89,27 @@ const ConfirmModalComponent = ({
89
89
  onConfirm: handleConfirm,
90
90
  onCancel: handleCancel
91
91
  }) }) : /* @__PURE__ */ jsxs(ComponentWrapper, { noPaddingHorizontal: true, flex: true, style: { gap: 16 }, children: [
92
- showCancel && /* @__PURE__ */ jsx(Button, { outlineBlack: true, minWidth: "120px", onClick: handleCancel, ...cancelButtonProps, children: cancelText }),
93
- /* @__PURE__ */ jsx(Button, { minWidth: "120px", onClick: () => handleConfirm(true), ...buttonProps, children: confirmText })
92
+ showCancel && /* @__PURE__ */ jsx(
93
+ Button,
94
+ {
95
+ outlineBlack: true,
96
+ minWidth: "120px",
97
+ onClick: handleCancel,
98
+ ...cancelButtonProps,
99
+ "data-cy": cancelButtonProps["data-cy"] ?? "confirm-modal-cancel",
100
+ children: cancelText
101
+ }
102
+ ),
103
+ /* @__PURE__ */ jsx(
104
+ Button,
105
+ {
106
+ minWidth: "120px",
107
+ onClick: () => handleConfirm(true),
108
+ ...buttonProps,
109
+ "data-cy": buttonProps["data-cy"] ?? "confirm-modal-confirm",
110
+ children: confirmText
111
+ }
112
+ )
94
113
  ] })
95
114
  ] }) }),
96
115
  showRedBar && /* @__PURE__ */ jsx(RedBar, { barColor, style: barStyle })
@@ -4,8 +4,13 @@ import { Text } from "../Text/Text.styles.js";
4
4
  import { useState } from "react";
5
5
  import scrollToElement from "scroll-to-element";
6
6
  import { FaqItems, FaqItem, FaqTitle, FaqDropdownIcon, FaqContent, FaqText } from "./FAQ.styles.js";
7
+ function slugFromTitle(title) {
8
+ if (title == null) return "";
9
+ const raw = typeof title === "string" ? title : "";
10
+ return raw.toLowerCase().trim().slice(0, 48).replace(/\s+/g, "-").replace(/[^a-z0-9_-]/g, "");
11
+ }
7
12
  const FAQ = (props) => {
8
- const { items, noScroll, scrollRef, modalViewMore, withTextStyling } = props;
13
+ const { items, noScroll, scrollRef, modalViewMore, withTextStyling, dataCyPrefix = "faq" } = props;
9
14
  const [activeIndex, setActiveIndex] = useState(null);
10
15
  const onTitleClick = (e, index) => {
11
16
  e.persist();
@@ -30,27 +35,34 @@ const FAQ = (props) => {
30
35
  fontWeight: "300",
31
36
  lineHeight: "16px"
32
37
  };
33
- return /* @__PURE__ */ jsx(FaqItems, { children: items.map((item, index) => (
34
- // eslint-disable-next-line @eslint-react/no-array-index-key
35
- /* @__PURE__ */ jsxs(FaqItem, { children: [
36
- /* @__PURE__ */ jsxs(
37
- FaqTitle,
38
- {
39
- isActive: activeIndex === index,
40
- onClick: (e) => onTitleClick(e, index),
41
- modalViewMore,
42
- children: [
43
- item.title,
44
- /* @__PURE__ */ jsx(FaqDropdownIcon, {})
45
- ]
46
- }
47
- ),
48
- /* @__PURE__ */ jsx(FaqContent, { isOpened: activeIndex === index, children: !Array.isArray(item.content) ? /* @__PURE__ */ jsx(FaqText, { children: item.content }) : /* @__PURE__ */ jsxs(Fragment, { children: [
49
- /* @__PURE__ */ jsx("br", {}),
50
- /* @__PURE__ */ jsx(Text, { style: withTextStyling ? textStyling : {}, children: /* @__PURE__ */ jsx("ul", { children: item.content.map((answer) => /* @__PURE__ */ jsx("li", { children: answer }, answer)) }) })
51
- ] }) })
52
- ] }, index)
53
- )) });
38
+ return /* @__PURE__ */ jsx(FaqItems, { "data-cy": `${dataCyPrefix}-accordion`, children: items.map((item, index) => {
39
+ const titleSlug = slugFromTitle(item.title);
40
+ const itemCy = titleSlug ? `${dataCyPrefix}-item-${titleSlug}` : `${dataCyPrefix}-item-${index}`;
41
+ return (
42
+ // eslint-disable-next-line @eslint-react/no-array-index-key -- FAQ order is content-defined
43
+ /* @__PURE__ */ jsxs(FaqItem, { "data-cy": `${itemCy}-wrap`, children: [
44
+ /* @__PURE__ */ jsxs(
45
+ FaqTitle,
46
+ {
47
+ type: "button",
48
+ "data-cy": `${itemCy}-toggle`,
49
+ "aria-expanded": activeIndex === index,
50
+ isActive: activeIndex === index,
51
+ onClick: (e) => onTitleClick(e, index),
52
+ modalViewMore,
53
+ children: [
54
+ item.title,
55
+ /* @__PURE__ */ jsx(FaqDropdownIcon, {})
56
+ ]
57
+ }
58
+ ),
59
+ /* @__PURE__ */ jsx(FaqContent, { "data-cy": `${itemCy}-panel`, isOpened: activeIndex === index, children: !Array.isArray(item.content) ? /* @__PURE__ */ jsx(FaqText, { children: item.content }) : /* @__PURE__ */ jsxs(Fragment, { children: [
60
+ /* @__PURE__ */ jsx("br", {}),
61
+ /* @__PURE__ */ jsx(Text, { style: withTextStyling ? textStyling : {}, children: /* @__PURE__ */ jsx("ul", { children: item.content.map((answer) => /* @__PURE__ */ jsx("li", { children: answer }, answer)) }) })
62
+ ] }) })
63
+ ] }, index)
64
+ );
65
+ }) });
54
66
  };
55
67
  FAQ.propTypes = {
56
68
  items: PropTypes.arrayOf(
@@ -58,7 +70,9 @@ FAQ.propTypes = {
58
70
  title: PropTypes.node.isRequired,
59
71
  content: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.node]).isRequired
60
72
  })
61
- ).isRequired
73
+ ).isRequired,
74
+ /** Prefix for data-cy: `{prefix}-accordion`, `{prefix}-item-{slug}-toggle`, etc. */
75
+ dataCyPrefix: PropTypes.string
62
76
  };
63
77
  var stdin_default = FAQ;
64
78
  export {
@@ -43,7 +43,17 @@ class ForgotPasswordModal extends PureComponent {
43
43
  ] }) }),
44
44
  /* @__PURE__ */ jsx(ModalInner, { modalBot: true, children: /* @__PURE__ */ jsxs(ModalLinkWrap, { children: [
45
45
  footerTextContent,
46
- /* @__PURE__ */ jsx(ModalLink, { type: "button", to: "#", as: "button", onClick: onModalSwitch, children: "Log in" })
46
+ /* @__PURE__ */ jsx(
47
+ ModalLink,
48
+ {
49
+ type: "button",
50
+ to: "#",
51
+ as: "button",
52
+ "data-cy": "forgot-password-modal-switch-to-login",
53
+ onClick: onModalSwitch,
54
+ children: "Log in"
55
+ }
56
+ )
47
57
  ] }) })
48
58
  ] }),
49
59
  /* @__PURE__ */ jsx(Column, { part: 8, children: /* @__PURE__ */ jsx(ModalInner, { children: popupSuccess.popup !== "forgot" ? /* @__PURE__ */ jsx(
@@ -69,12 +79,23 @@ class ForgotPasswordModal extends PureComponent {
69
79
  error: !!errors.email,
70
80
  touched: !!touched.email,
71
81
  onChange: handleChange,
72
- onBlur: handleBlur
82
+ onBlur: handleBlur,
83
+ "data-cy": "forgot-password-modal-email-input"
73
84
  }
74
85
  ),
75
86
  /* @__PURE__ */ jsx(FormError, { show: errors.email && touched.email, children: errors.email })
76
87
  ] }) }),
77
- /* @__PURE__ */ jsx(ComponentWrapper, { noPaddingBottom: true, noPaddingHorizontal: true, children: /* @__PURE__ */ jsx(Button, { type: "submit", id: "forgot-password-submit-btn", fluid: true, disabled: submitDisabled, children: isActionLoading === "forgot" ? "Sending Reset Link" : "Send Reset Link" }) }),
88
+ /* @__PURE__ */ jsx(ComponentWrapper, { noPaddingBottom: true, noPaddingHorizontal: true, children: /* @__PURE__ */ jsx(
89
+ Button,
90
+ {
91
+ type: "submit",
92
+ id: "forgot-password-submit-btn",
93
+ "data-cy": "forgot-password-modal-submit",
94
+ fluid: true,
95
+ disabled: submitDisabled,
96
+ children: isActionLoading === "forgot" ? "Sending Reset Link" : "Send Reset Link"
97
+ }
98
+ ) }),
78
99
  /* @__PURE__ */ jsx(ComponentWrapper, { paddingVeticalHalf: true, noPaddingHorizontal: true, children: /* @__PURE__ */ jsx(FormError, { show: popupError.popup === "forgot", children: popupError.message }) })
79
100
  ] });
80
101
  }
@@ -190,7 +190,7 @@ function HeaderView({
190
190
  withSidebar && /* @__PURE__ */ jsx(SidebarToggle, { isSidebarExpanded, onClick: sidebarHandler, children: /* @__PURE__ */ jsx(HamburgerIcon, {}) }),
191
191
  /* @__PURE__ */ jsxs(HeaderInner, { withSidebar, children: [
192
192
  /* @__PURE__ */ jsxs(MainNavWrap, { hasProgress: progress, children: [
193
- /* @__PURE__ */ jsx(Logo, { isHighlight: currentActiveMenu === "main", children: /* @__PURE__ */ jsx(Link, { to: newPlatformUrl || "/", "data-cy": "logo-nav-link", children: /* @__PURE__ */ jsx(stdin_default$1, {}) }) }),
193
+ /* @__PURE__ */ jsx(Logo, { isHighlight: currentActiveMenu === "main", children: useRelativePathForMenu ? /* @__PURE__ */ jsx("a", { href: platformURL, "data-cy": "logo-nav-link", children: /* @__PURE__ */ jsx(stdin_default$1, {}) }) : /* @__PURE__ */ jsx(Link, { to: "/", "data-cy": "logo-nav-link", children: /* @__PURE__ */ jsx(stdin_default$1, {}) }) }),
194
194
  !progress && /* @__PURE__ */ jsx(HeaderMainNav, { children: /* @__PURE__ */ jsx(MenuNav, { children: /* @__PURE__ */ jsxs("ul", { children: [
195
195
  /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
196
196
  Link,
@@ -22,7 +22,12 @@ const InputPassword = (props) => {
22
22
  touched = false,
23
23
  info = "",
24
24
  showStrength = false,
25
- autoComplete = "current-password"
25
+ autoComplete = "current-password",
26
+ onChange,
27
+ onBlur,
28
+ onFocus,
29
+ toggleDataCy,
30
+ ...domRest
26
31
  } = props;
27
32
  const [isPasswordVisible, setIsPasswordVisible] = useState(false);
28
33
  const [isCapslockOn, setIsCapslockOn] = useState(false);
@@ -37,16 +42,16 @@ const InputPassword = (props) => {
37
42
  const handleCapslock = (e) => {
38
43
  setIsCapslockOn(e.getModifierState("CapsLock"));
39
44
  };
40
- const onFocus = (e, field) => {
41
- props.onFocus?.(e, field);
45
+ const handleFocus = (e, field) => {
46
+ onFocus?.(e, field);
42
47
  setHasFocus(true);
43
48
  };
44
- const onBlur = (e, field) => {
45
- if (props.onBlur) props.onBlur(e, field);
49
+ const handleBlur = (e, field) => {
50
+ onBlur?.(e, field);
46
51
  setHasFocus(false);
47
52
  };
48
- const onChange = (e, field) => {
49
- props.onChange?.(e, field);
53
+ const handleChange = (e, field) => {
54
+ onChange?.(e, field);
50
55
  };
51
56
  const passwordStrengthFunction = () => {
52
57
  if (value.length === 0) {
@@ -67,19 +72,28 @@ const InputPassword = (props) => {
67
72
  /* @__PURE__ */ jsx(InputWrap, { error: error && touched, children: /* @__PURE__ */ jsx(
68
73
  "input",
69
74
  {
75
+ ...domRest,
70
76
  type: isPasswordVisible ? "text" : "password",
71
77
  id,
72
78
  name,
73
79
  value,
74
80
  autoComplete,
75
- onChange,
76
- onBlur,
77
- onFocus,
81
+ onChange: handleChange,
82
+ onBlur: handleBlur,
83
+ onFocus: handleFocus,
78
84
  onKeyUp: handleCapslock,
79
85
  onKeyDown: handleCapslock
80
86
  }
81
87
  ) }),
82
- /* @__PURE__ */ jsx(ShowPassword, { onClick: togglePasswordVisibility, children: isPasswordVisible ? /* @__PURE__ */ jsx("i", { className: "ion ion-md-eye-off" }) : /* @__PURE__ */ jsx("i", { className: "ion ion-md-eye" }) }),
88
+ /* @__PURE__ */ jsx(
89
+ ShowPassword,
90
+ {
91
+ type: "button",
92
+ onClick: togglePasswordVisibility,
93
+ "data-cy": toggleDataCy ?? `${id}-password-visibility-toggle`,
94
+ children: isPasswordVisible ? /* @__PURE__ */ jsx("i", { className: "ion ion-md-eye-off" }) : /* @__PURE__ */ jsx("i", { className: "ion ion-md-eye" })
95
+ }
96
+ ),
83
97
  /* @__PURE__ */ jsxs(BottomWrap, { children: [
84
98
  showStrength ? /* @__PURE__ */ jsxs(PasswordStrengthWrap, { children: [
85
99
  /* @__PURE__ */ jsxs(PasswordStrengthIndicators, { passwordStrength, children: [
@@ -112,7 +126,8 @@ InputPassword.propTypes = {
112
126
  onFocus: PropTypes.func,
113
127
  info: PropTypes.string,
114
128
  showStrength: PropTypes.bool,
115
- autoComplete: PropTypes.string
129
+ autoComplete: PropTypes.string,
130
+ toggleDataCy: PropTypes.string
116
131
  };
117
132
  var stdin_default = InputPassword;
118
133
  export {
@@ -23,7 +23,18 @@ const Separator = styled.span`
23
23
  `}
24
24
  `;
25
25
  const OtpInput = (props) => {
26
- const { value, numInputs = 4, RenderSeparator = Separator, inputType = "text", shouldAutoFocus = true, label } = props;
26
+ const {
27
+ value,
28
+ numInputs = 4,
29
+ RenderSeparator = Separator,
30
+ inputType = "text",
31
+ shouldAutoFocus = true,
32
+ label,
33
+ /** Per-digit selectors: `{prefix}-digit-0`, … */
34
+ dataCyPrefix,
35
+ onChange,
36
+ ...otpProps
37
+ } = props;
27
38
  const [labelUp, setLabelUp] = useState(false);
28
39
  const [hasFocus, setHasFocus] = useState(false);
29
40
  const checkLabel = (focus) => {
@@ -40,10 +51,9 @@ const OtpInput = (props) => {
40
51
  if (value && !labelUp) setLabelUp(true);
41
52
  }, [value, labelUp]);
42
53
  const handleChange = (e, field) => {
43
- const { onChange } = props;
44
54
  onChange?.(e, field);
45
55
  };
46
- return /* @__PURE__ */ jsxs(Wrapper, { children: [
56
+ return /* @__PURE__ */ jsxs(Wrapper, { ...dataCyPrefix ? { "data-cy": `${dataCyPrefix}-wrapper` } : {}, children: [
47
57
  label && /* @__PURE__ */ jsx(Label, { isUp: true, style: { lineHeight: "inherit", top: "-5px" }, children: label }),
48
58
  /* @__PURE__ */ jsx(InputWrap, { children: /* @__PURE__ */ jsx(
49
59
  Input,
@@ -55,10 +65,14 @@ const OtpInput = (props) => {
55
65
  shouldAutoFocus,
56
66
  inputStyle,
57
67
  renderSeparator: /* @__PURE__ */ jsx(RenderSeparator, {}),
58
- renderInput: (_props) => {
59
- return /* @__PURE__ */ jsx("input", { ..._props });
60
- },
61
- ...props
68
+ renderInput: (inputProps, index) => /* @__PURE__ */ jsx(
69
+ "input",
70
+ {
71
+ ...inputProps,
72
+ ...dataCyPrefix ? { "data-cy": `${dataCyPrefix}-digit-${index}` } : {}
73
+ }
74
+ ),
75
+ ...otpProps
62
76
  }
63
77
  ) })
64
78
  ] });
@@ -22,6 +22,11 @@ const SelectMenuListWithScroll = (props) => {
22
22
  SelectMenuListWithScroll.propTypes = {
23
23
  children: PropTypes.node.isRequired
24
24
  };
25
+ function createControlWithDataCy(dataCy) {
26
+ return function ControlWithDataCy(ctlProps) {
27
+ return /* @__PURE__ */ jsx(components.Control, { ...ctlProps, innerProps: { ...ctlProps.innerProps, "data-cy": dataCy } });
28
+ };
29
+ }
25
30
  const Select = (props) => {
26
31
  const {
27
32
  id,
@@ -36,6 +41,7 @@ const Select = (props) => {
36
41
  menuHeight,
37
42
  creatable = false,
38
43
  formatCreateLabel = null,
44
+ dataCy,
39
45
  ...otherProps
40
46
  } = props;
41
47
  const [labelUp, setLabelUp] = useState(!!value);
@@ -87,7 +93,8 @@ const Select = (props) => {
87
93
  const selectComponents = {
88
94
  DropdownIndicator,
89
95
  IndicatorSeparator: null,
90
- MenuList: SelectMenuListWithScroll
96
+ MenuList: SelectMenuListWithScroll,
97
+ ...dataCy ? { Control: createControlWithDataCy(dataCy) } : {}
91
98
  };
92
99
  const currentValue = creatable ? options?.find((option) => option.value === value) || (value ? { value, label: value } : null) : options?.find((option) => option.value === value);
93
100
  return /* @__PURE__ */ jsxs(Wrapper, { children: [
@@ -136,7 +143,9 @@ Select.propTypes = {
136
143
  formatCreateLabel: PropTypes.func,
137
144
  onChange: PropTypes.func,
138
145
  onBlur: PropTypes.func,
139
- onFocus: PropTypes.func
146
+ onFocus: PropTypes.func,
147
+ /** Playwright / autocapture: selector on the react-select control */
148
+ dataCy: PropTypes.string
140
149
  };
141
150
  export {
142
151
  Select,