@thecb/components 11.3.1 → 11.3.2-beta.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thecb/components",
3
- "version": "11.3.1",
3
+ "version": "11.3.2-beta.1",
4
4
  "description": "Common lib for CityBase react components",
5
5
  "main": "dist/index.cjs.js",
6
6
  "typings": "dist/index.d.ts",
@@ -3,20 +3,46 @@ import React, {
3
3
  useEffect,
4
4
  useRef,
5
5
  forwardRef,
6
- useImperativeHandle
6
+ useImperativeHandle,
7
+ useCallback
7
8
  } from "react";
8
9
  import { Box } from "../../atoms/layouts";
10
+ import Text from "../../atoms/text";
9
11
  import styled from "styled-components";
10
12
  import { useTurnstileScript } from "../../../hooks";
11
13
  import { noop } from "../../../util/general";
14
+ import { ERROR_COLOR } from "../../../constants/colors";
12
15
 
13
16
  const TurnstileWidgetContainer = styled(Box)`
14
17
  display: flex;
18
+ flex-direction: column;
15
19
  padding: ${({ padding }) => padding};
16
20
  justify-content: ${({ justify }) => justify};
21
+ align-items: ${({ align }) => align};
17
22
  width: 100%;
18
23
  `;
19
24
 
25
+ const FATAL_ERROR_CODES = [
26
+ // Configuration errors
27
+ /^100/,
28
+ /^105/,
29
+ /^110100$/,
30
+ /^110110$/,
31
+ /^110200$/,
32
+ /^110420$/,
33
+ /^110430$/,
34
+ /^400020$/,
35
+ /^400030$/,
36
+ /^400040$/,
37
+ // Browser/environment errors
38
+ /^110500$/,
39
+ /^110510$/,
40
+ /^200010$/,
41
+ /^200100$/,
42
+ // Internal errors
43
+ /^120/
44
+ ];
45
+
20
46
  const TurnstileWidget = forwardRef(
21
47
  (
22
48
  {
@@ -27,64 +53,150 @@ const TurnstileWidget = forwardRef(
27
53
  onExpired = noop,
28
54
  padding = "1rem",
29
55
  justify = "flex-end",
56
+ align = "flex-end",
30
57
  size = "normal",
31
58
  tabindex = 0,
32
- retry = "auto",
33
- theme = "auto",
34
- extraStyles = ""
59
+ retry = "never",
60
+ theme = "light",
61
+ extraStyles = "",
62
+ maxErrorRetries = 3
35
63
  },
36
64
  ref
37
65
  ) => {
38
66
  if (!verifyURL || !siteKey) return null;
39
67
 
40
68
  const widgetRef = useRef(null);
41
- const [widgetId, setWidgetId] = useState(null);
42
- const isTurnstileLoaded = useTurnstileScript(verifyURL);
69
+ const widgetIdRef = useRef(null);
70
+ const retryCountRef = useRef(0);
71
+ const [error, setError] = useState(null);
72
+
73
+ const { scriptLoaded, scriptError } = useTurnstileScript(verifyURL);
74
+
75
+ // Clear the error state and reset retry count
76
+ const clearError = () => {
77
+ setError(null);
78
+ retryCountRef.current = 0;
79
+ };
80
+
81
+ // Handle errors based on the retry strategy
82
+ const handleError = errorCode => {
83
+ retryCountRef.current += 1;
84
+ const currentRetryCount = retryCountRef.current;
85
+
86
+ // If we haven't exceeded max retries, reset and try again
87
+ if (currentRetryCount <= maxErrorRetries) {
88
+ window.turnstile.reset(widgetIdRef.current);
89
+ setError(null);
90
+ return;
91
+ }
92
+
93
+ // Max retries exceeded - show appropriate error message
94
+ if (errorCode) {
95
+ const errorMessage = getErrorMessage(errorCode);
96
+ setError(errorMessage);
97
+ }
98
+ };
99
+
100
+ const getErrorMessage = errorCode => {
101
+ const isFatalError = FATAL_ERROR_CODES.some(pattern =>
102
+ pattern.test(errorCode)
103
+ );
104
+
105
+ if (isFatalError) {
106
+ return "Browser or system error. Please refresh the page or update your browser.";
107
+ }
108
+ return "Something went wrong. Please refresh and try again.";
109
+ };
110
+
111
+ // Reset the Turnstile widget when requested
112
+ const resetWidget = useCallback(() => {
113
+ if (widgetIdRef.current && window.turnstile) {
114
+ window.turnstile.reset(widgetIdRef.current);
115
+ clearError();
116
+ }
117
+ }, [clearError]);
43
118
 
44
119
  // Allow the parent component to reset the Turnstile widget
45
120
  useImperativeHandle(
46
121
  ref,
47
122
  () => ({
48
- reset: () => {
49
- if (widgetId && window.turnstile) {
50
- window.turnstile.reset(widgetId);
51
- }
52
- }
123
+ reset: resetWidget
53
124
  }),
54
- [widgetId]
125
+ [resetWidget]
55
126
  );
56
127
 
57
128
  useEffect(() => {
58
- if (!widgetRef.current || !window.turnstile) return;
59
- if (!widgetId) {
60
- const newWidgetId = window.turnstile.render(widgetRef.current, {
129
+ if (scriptError) {
130
+ setError(
131
+ "Unable to load security verification. Please refresh the page."
132
+ );
133
+ onError?.({ type: "script_load_failed", error: scriptError });
134
+ return;
135
+ }
136
+
137
+ if (widgetIdRef.current || !window.turnstile || !scriptLoaded) return;
138
+
139
+ try {
140
+ widgetIdRef.current = window.turnstile.render(widgetRef.current, {
61
141
  sitekey: siteKey,
62
142
  size: size,
63
143
  retry: retry,
64
144
  tabindex: tabindex,
65
145
  theme: theme,
66
- callback: token => onSuccess(token),
67
- "error-callback": () => onError(),
68
- "expired-callback": () => onExpired()
146
+ callback: token => {
147
+ clearError();
148
+ onSuccess(token);
149
+ },
150
+ "error-callback": errorCode => {
151
+ if (retry === "never") {
152
+ handleError(errorCode);
153
+ }
154
+ onError?.(errorCode);
155
+ },
156
+ "expired-callback": () => {
157
+ clearError();
158
+ onExpired?.();
159
+ }
69
160
  });
70
- setWidgetId(newWidgetId);
161
+ } catch (error) {
162
+ setError(
163
+ "Unable to load security verification. Please refresh the page."
164
+ );
165
+ onError?.({ type: "render_failed", error });
71
166
  }
167
+
72
168
  return () => {
73
- if (widgetId && window.turnstile) {
74
- window.turnstile.remove(widgetId);
75
- setWidgetId(null);
169
+ if (widgetIdRef.current && window.turnstile) {
170
+ window.turnstile.remove(widgetIdRef.current);
171
+ widgetIdRef.current = null;
172
+ clearError();
76
173
  }
77
174
  };
78
- }, [isTurnstileLoaded, siteKey, widgetId]);
175
+ }, [scriptLoaded, siteKey]);
79
176
 
80
177
  return (
81
- <TurnstileWidgetContainer
82
- padding={padding}
83
- justify={justify}
84
- extraStyles={extraStyles}
85
- >
86
- <div ref={widgetRef} />
87
- </TurnstileWidgetContainer>
178
+ <>
179
+ <TurnstileWidgetContainer
180
+ padding={padding}
181
+ justify={justify}
182
+ align={align}
183
+ extraStyles={extraStyles}
184
+ >
185
+ <div ref={widgetRef} />
186
+ {error && (
187
+ <Text
188
+ color={ERROR_COLOR}
189
+ variant="pXS"
190
+ extraStyles={`
191
+ word-break: break-word;
192
+ text-align: end;
193
+ `}
194
+ >
195
+ {error}
196
+ </Text>
197
+ )}
198
+ </TurnstileWidgetContainer>
199
+ </>
88
200
  );
89
201
  }
90
202
  );
@@ -6,31 +6,44 @@ import { useEffect, useState } from "react";
6
6
  * @param {string} verifyURL - The URL of the Turnstile verification script.
7
7
  */
8
8
  const useTurnstileScript = verifyURL => {
9
- const [isLoaded, setIsLoaded] = useState(false);
9
+ const [scriptLoaded, setScriptLoaded] = useState(false);
10
+ const [scriptError, setScriptError] = useState(false);
11
+
12
+ const handleScriptError = () => {
13
+ setScriptError(true);
14
+ setScriptLoaded(false);
15
+ };
10
16
 
11
17
  useEffect(() => {
12
18
  if (typeof window === "undefined") {
13
- setIsLoaded(false);
19
+ setScriptLoaded(false);
14
20
  return;
15
21
  }
16
22
  if (window.turnstile && window.turnstile.render) {
17
- setIsLoaded(true);
23
+ setScriptLoaded(true);
18
24
  return;
19
25
  }
20
26
 
21
27
  const script = document.createElement("script");
22
28
  script.src = `${verifyURL}?render=explicit`;
23
29
  script.onload = () => {
24
- setIsLoaded(true);
30
+ setScriptLoaded(true);
31
+ };
32
+ script.onerror = () => {
33
+ handleScriptError();
34
+ };
35
+ script.onabort = () => {
36
+ handleScriptError();
25
37
  };
26
38
  script.defer = true;
27
39
  document.getElementsByTagName("head")[0].appendChild(script);
28
40
 
29
41
  return () => {
30
- setIsLoaded(false);
42
+ setScriptLoaded(false);
43
+ setScriptError(false);
31
44
  };
32
45
  }, [verifyURL]);
33
46
 
34
- return isLoaded;
47
+ return { scriptLoaded, scriptError };
35
48
  };
36
49
  export default useTurnstileScript;
package/src/.DS_Store DELETED
Binary file
Binary file
Binary file