@hcaptcha/react-hcaptcha 0.3.8 → 1.1.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.
package/README.md CHANGED
@@ -47,12 +47,71 @@ import { default as RenamedCaptcha } from '../utils/captcha';
47
47
  </FormComponent>
48
48
  ```
49
49
 
50
+ #### Programmatic Usage
51
+ In the event you want to call the hCaptcha client API directly, you can do so by using the hook `useRef` and waiting for `onLoad` to be called. By waiting for `onLoad` the hCaptcha API will be ready and the hCaptcha client will have been setup. See the following example:
52
+
53
+ ```js
54
+ import { useEffect, useRef, useState } from "react";
55
+ import HCaptcha from "@hcaptcha/react-hcaptcha";
56
+
57
+ export default function Form() {
58
+ const [token, setToken] = useState(null);
59
+ const captchaRef = useRef(null);
60
+
61
+ const onLoad = () => {
62
+ // this reaches out to the hCaptcha JS API and runs the
63
+ // execute function on it. you can use other functions as
64
+ // documented here:
65
+ // https://docs.hcaptcha.com/configuration#jsapi
66
+ captchaRef.current.execute();
67
+ };
68
+
69
+ useEffect(() => {
70
+
71
+ if (token)
72
+ console.log(`hCaptcha Token: ${token}`);
73
+
74
+ }, [token]);
75
+
76
+ return (
77
+ <form>
78
+ <HCaptcha
79
+ sitekey="your-sitekey"
80
+ onLoad={onLoad}
81
+ onVerify={setToken}
82
+ ref={captchaRef}
83
+ />
84
+ </form>
85
+ );
86
+ }
87
+ ```
88
+
50
89
  #### Advanced usage
51
90
 
52
91
  In most real-world implementations, you'll probably be using a form library such as [Formik](https://github.com/jaredpalmer/formik) or [React Hook Form](https://github.com/react-hook-form/react-hook-form).
53
92
 
54
93
  In these instances, you'll most likely want to use `ref` to handle the callbacks as well as handle field-level validation of a `captcha` field. For an example of this, you can view this [CodeSandbox](https://codesandbox.io/s/react-hcaptchaform-example-forked-ngxge?file=/src/Form.jsx). This `ref` will point to an instance of the [hCaptcha API](https://docs.hcaptcha.com/configuration#jsapi) where can you interact directly with it.
55
94
 
95
+ #### Passing in fields like `rqdata` to `execute()`
96
+
97
+ Passing an object into the `execute(yourObj)` call will send it through to the underlying JS API. This enables support for Enterprise features like `rqdata`. A simple example is below:
98
+
99
+ ```
100
+ const {sitekey, rqdata} = props;
101
+ const captchaRef = React.useRef<HCaptcha>(null);
102
+
103
+ const onLoad = () => {
104
+ const executePayload = {};
105
+ if (rqdata) {
106
+ executePayload['rqdata'] = rqdata;
107
+ }
108
+ captchaRef.current?.execute(executePayload);
109
+ };
110
+
111
+ return <HCaptcha ref={captchaRef} onLoad={onLoad} sitekey={sitekey} {...props} />;
112
+ ```
113
+
114
+
56
115
  ### Props
57
116
 
58
117
  |Name|Values/Type|Required|Default|Description|
@@ -81,6 +140,9 @@ In these instances, you'll most likely want to use `ref` to handle the callbacks
81
140
  |`onVerify`|`token, eKey`|When challenge is completed. The response `token` and an `eKey` (session id) are passed along.|
82
141
  |`onExpire`|-|When the current token expires.|
83
142
  |`onLoad`|-|When the hCaptcha API loads.|
143
+ |`onOpen`|-|When the user display of a challenge starts.|
144
+ |`onClose`|-|When the user dismisses a challenge.|
145
+ |`onChalExpired`|-|When the user display of a challenge times out with no answer.|
84
146
 
85
147
  ### Methods
86
148
 
package/dist/index.js CHANGED
@@ -4,8 +4,6 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
 
5
5
  var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
6
6
 
7
- var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
8
-
9
7
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
10
8
 
11
9
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
@@ -18,10 +16,6 @@ var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime
18
16
 
19
17
  var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
20
18
 
21
- function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
22
-
23
- function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
24
-
25
19
  function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2["default"])(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2["default"])(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2["default"])(this, result); }; }
26
20
 
27
21
  function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
@@ -47,7 +41,7 @@ var mountCaptchaScript = function mountCaptchaScript() {
47
41
  });
48
42
  };
49
43
 
50
- var domain = params.apihost || "https://hcaptcha.com";
44
+ var domain = params.apihost || "https://js.hcaptcha.com";
51
45
  delete params.apihost;
52
46
  var script = document.createElement("script");
53
47
  script.src = "".concat(domain, "/1/api.js?render=explicit&onload=hcaptchaOnLoad");
@@ -70,12 +64,16 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
70
64
 
71
65
  _this.renderCaptcha = _this.renderCaptcha.bind((0, _assertThisInitialized2["default"])(_this));
72
66
  _this.resetCaptcha = _this.resetCaptcha.bind((0, _assertThisInitialized2["default"])(_this));
73
- _this.removeCaptcha = _this.removeCaptcha.bind((0, _assertThisInitialized2["default"])(_this)); // Event Handlers
67
+ _this.removeCaptcha = _this.removeCaptcha.bind((0, _assertThisInitialized2["default"])(_this));
68
+ _this.isReady = _this.isReady.bind((0, _assertThisInitialized2["default"])(_this)); // Event Handlers
74
69
 
75
70
  _this.handleOnLoad = _this.handleOnLoad.bind((0, _assertThisInitialized2["default"])(_this));
76
71
  _this.handleSubmit = _this.handleSubmit.bind((0, _assertThisInitialized2["default"])(_this));
77
72
  _this.handleExpire = _this.handleExpire.bind((0, _assertThisInitialized2["default"])(_this));
78
73
  _this.handleError = _this.handleError.bind((0, _assertThisInitialized2["default"])(_this));
74
+ _this.handleOpen = _this.handleOpen.bind((0, _assertThisInitialized2["default"])(_this));
75
+ _this.handleClose = _this.handleClose.bind((0, _assertThisInitialized2["default"])(_this));
76
+ _this.handleChallengeExpired = _this.handleChallengeExpired.bind((0, _assertThisInitialized2["default"])(_this));
79
77
  var isApiReady = typeof hcaptcha !== 'undefined';
80
78
  _this.ref = React.createRef();
81
79
  _this.state = {
@@ -131,11 +129,12 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
131
129
  }, {
132
130
  key: "componentWillUnmount",
133
131
  value: function componentWillUnmount() {
134
- var _this$state = this.state,
135
- isApiReady = _this$state.isApiReady,
136
- isRemoved = _this$state.isRemoved,
137
- captchaId = _this$state.captchaId;
138
- if (!isApiReady || isRemoved) return; // Reset any stored variables / timers when unmounting
132
+ var captchaId = this.state.captchaId;
133
+
134
+ if (!this.isReady()) {
135
+ return;
136
+ } // Reset any stored variables / timers when unmounting
137
+
139
138
 
140
139
  hcaptcha.reset(captchaId);
141
140
  hcaptcha.remove(captchaId);
@@ -170,39 +169,50 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
170
169
  }
171
170
  }, {
172
171
  key: "renderCaptcha",
173
- value: function renderCaptcha() {
172
+ value: function renderCaptcha(onReady) {
174
173
  var isApiReady = this.state.isApiReady;
175
- if (!isApiReady) return; //Render hCaptcha widget and provide necessary callbacks - hCaptcha
176
-
177
- var captchaId = hcaptcha.render(this.ref.current, _objectSpread(_objectSpread({}, this.props), {}, {
174
+ if (!isApiReady) return;
175
+ var renderParams = Object.assign({
176
+ "open-callback": this.handleOpen,
177
+ "close-callback": this.handleClose,
178
178
  "error-callback": this.handleError,
179
+ "chalexpired-callback": this.handleChallengeExpired,
179
180
  "expired-callback": this.handleExpire,
180
181
  "callback": this.handleSubmit
181
- }));
182
+ }, this.props, {
183
+ hl: this.props.hl || this.props.languageOverride,
184
+ languageOverride: undefined
185
+ }); //Render hCaptcha widget and provide necessary callbacks - hCaptcha
186
+
187
+ var captchaId = hcaptcha.render(this.ref.current, renderParams);
182
188
  this.setState({
183
189
  isRemoved: false,
184
190
  captchaId: captchaId
191
+ }, function () {
192
+ onReady && onReady();
185
193
  });
186
194
  }
187
195
  }, {
188
196
  key: "resetCaptcha",
189
197
  value: function resetCaptcha() {
190
- var _this$state2 = this.state,
191
- isApiReady = _this$state2.isApiReady,
192
- isRemoved = _this$state2.isRemoved,
193
- captchaId = _this$state2.captchaId;
194
- if (!isApiReady || isRemoved) return; // Reset captcha state, removes stored token and unticks checkbox
198
+ var captchaId = this.state.captchaId;
199
+
200
+ if (!this.isReady()) {
201
+ return;
202
+ } // Reset captcha state, removes stored token and unticks checkbox
203
+
195
204
 
196
205
  hcaptcha.reset(captchaId);
197
206
  }
198
207
  }, {
199
208
  key: "removeCaptcha",
200
209
  value: function removeCaptcha(callback) {
201
- var _this$state3 = this.state,
202
- isApiReady = _this$state3.isApiReady,
203
- isRemoved = _this$state3.isRemoved,
204
- captchaId = _this$state3.captchaId;
205
- if (!isApiReady || isRemoved) return;
210
+ var captchaId = this.state.captchaId;
211
+
212
+ if (!this.isReady()) {
213
+ return;
214
+ }
215
+
206
216
  this.setState({
207
217
  isRemoved: true
208
218
  }, function () {
@@ -218,20 +228,21 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
218
228
  this.setState({
219
229
  isApiReady: true
220
230
  }, function () {
221
- // trigger onLoad if it exists
222
- var onLoad = _this3.props.onLoad;
223
- if (onLoad) onLoad(); // render captcha
224
-
225
- _this3.renderCaptcha();
231
+ // render captcha and wait for captcha id
232
+ _this3.renderCaptcha(function () {
233
+ // trigger onLoad if it exists
234
+ var onLoad = _this3.props.onLoad;
235
+ if (onLoad) onLoad();
236
+ });
226
237
  });
227
238
  }
228
239
  }, {
229
240
  key: "handleSubmit",
230
241
  value: function handleSubmit(event) {
231
242
  var onVerify = this.props.onVerify;
232
- var _this$state4 = this.state,
233
- isRemoved = _this$state4.isRemoved,
234
- captchaId = _this$state4.captchaId;
243
+ var _this$state = this.state,
244
+ isRemoved = _this$state.isRemoved,
245
+ captchaId = _this$state.captchaId;
235
246
  if (typeof hcaptcha === 'undefined' || isRemoved) return;
236
247
  var token = hcaptcha.getResponse(captchaId); //Get response token from hCaptcha widget
237
248
 
@@ -243,11 +254,12 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
243
254
  key: "handleExpire",
244
255
  value: function handleExpire() {
245
256
  var onExpire = this.props.onExpire;
246
- var _this$state5 = this.state,
247
- isApiReady = _this$state5.isApiReady,
248
- isRemoved = _this$state5.isRemoved,
249
- captchaId = _this$state5.captchaId;
250
- if (!isApiReady || isRemoved) return;
257
+ var captchaId = this.state.captchaId;
258
+
259
+ if (!this.isReady()) {
260
+ return;
261
+ }
262
+
251
263
  hcaptcha.reset(captchaId); // If hCaptcha runs into error, reset captcha - hCaptcha
252
264
 
253
265
  if (onExpire) onExpire();
@@ -256,24 +268,60 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
256
268
  key: "handleError",
257
269
  value: function handleError(event) {
258
270
  var onError = this.props.onError;
259
- var _this$state6 = this.state,
260
- isApiReady = _this$state6.isApiReady,
261
- isRemoved = _this$state6.isRemoved,
262
- captchaId = _this$state6.captchaId;
263
- if (!isApiReady || isRemoved) return;
271
+ var captchaId = this.state.captchaId;
272
+
273
+ if (!this.isReady()) {
274
+ return;
275
+ }
276
+
264
277
  hcaptcha.reset(captchaId); // If hCaptcha runs into error, reset captcha - hCaptcha
265
278
 
266
279
  if (onError) onError(event);
267
280
  }
281
+ }, {
282
+ key: "isReady",
283
+ value: function isReady() {
284
+ var _this$state2 = this.state,
285
+ isApiReady = _this$state2.isApiReady,
286
+ isRemoved = _this$state2.isRemoved;
287
+ return isApiReady && !isRemoved;
288
+ }
289
+ }, {
290
+ key: "handleOpen",
291
+ value: function handleOpen() {
292
+ if (!this.isReady() || !this.props.onOpen) {
293
+ return;
294
+ }
295
+
296
+ this.props.onOpen();
297
+ }
298
+ }, {
299
+ key: "handleClose",
300
+ value: function handleClose() {
301
+ if (!this.isReady() || !this.props.onClose) {
302
+ return;
303
+ }
304
+
305
+ this.props.onClose();
306
+ }
307
+ }, {
308
+ key: "handleChallengeExpired",
309
+ value: function handleChallengeExpired() {
310
+ if (!this.isReady() || !this.props.onChalExpired) {
311
+ return;
312
+ }
313
+
314
+ this.props.onChalExpired();
315
+ }
268
316
  }, {
269
317
  key: "execute",
270
318
  value: function execute() {
271
319
  var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
272
- var _this$state7 = this.state,
273
- isApiReady = _this$state7.isApiReady,
274
- isRemoved = _this$state7.isRemoved,
275
- captchaId = _this$state7.captchaId;
276
- if (!isApiReady || isRemoved) return;
320
+ var captchaId = this.state.captchaId;
321
+
322
+ if (!this.isReady()) {
323
+ return;
324
+ }
277
325
 
278
326
  if (opts && (0, _typeof2["default"])(opts) !== "object") {
279
327
  opts = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hcaptcha/react-hcaptcha",
3
- "version": "0.3.8",
3
+ "version": "1.1.0",
4
4
  "types": "types/index.d.ts",
5
5
  "main": "dist/index.js",
6
6
  "files": [
package/src/index.js CHANGED
@@ -17,7 +17,7 @@ const mountCaptchaScript = (params={}) => {
17
17
  });
18
18
  };
19
19
 
20
- const domain = params.apihost || "https://hcaptcha.com";
20
+ const domain = params.apihost || "https://js.hcaptcha.com";
21
21
  delete params.apihost;
22
22
 
23
23
  const script = document.createElement("script");
@@ -39,12 +39,16 @@ class HCaptcha extends React.Component {
39
39
  this.renderCaptcha = this.renderCaptcha.bind(this);
40
40
  this.resetCaptcha = this.resetCaptcha.bind(this);
41
41
  this.removeCaptcha = this.removeCaptcha.bind(this);
42
+ this.isReady = this.isReady.bind(this);
42
43
 
43
44
  // Event Handlers
44
45
  this.handleOnLoad = this.handleOnLoad.bind(this);
45
46
  this.handleSubmit = this.handleSubmit.bind(this);
46
47
  this.handleExpire = this.handleExpire.bind(this);
47
48
  this.handleError = this.handleError.bind(this);
49
+ this.handleOpen = this.handleOpen.bind(this);
50
+ this.handleClose = this.handleClose.bind(this);
51
+ this.handleChallengeExpired = this.handleChallengeExpired.bind(this);
48
52
 
49
53
  const isApiReady = typeof hcaptcha !== 'undefined';
50
54
 
@@ -88,8 +92,11 @@ class HCaptcha extends React.Component {
88
92
  }
89
93
 
90
94
  componentWillUnmount() {
91
- const { isApiReady, isRemoved, captchaId } = this.state;
92
- if(!isApiReady || isRemoved) return
95
+ const { captchaId } = this.state;
96
+
97
+ if (!this.isReady()) {
98
+ return;
99
+ }
93
100
 
94
101
  // Reset any stored variables / timers when unmounting
95
102
  hcaptcha.reset(captchaId);
@@ -119,34 +126,46 @@ class HCaptcha extends React.Component {
119
126
  }
120
127
  }
121
128
 
122
- renderCaptcha() {
129
+ renderCaptcha(onReady) {
123
130
  const { isApiReady } = this.state;
124
131
  if (!isApiReady) return;
125
132
 
133
+ const renderParams = Object.assign({
134
+ "open-callback" : this.handleOpen,
135
+ "close-callback" : this.handleClose,
136
+ "error-callback" : this.handleError,
137
+ "chalexpired-callback": this.handleChallengeExpired,
138
+ "expired-callback" : this.handleExpire,
139
+ "callback" : this.handleSubmit,
140
+ }, this.props, {
141
+ hl: this.props.hl || this.props.languageOverride,
142
+ languageOverride: undefined
143
+ });
144
+
126
145
  //Render hCaptcha widget and provide necessary callbacks - hCaptcha
127
- const captchaId = hcaptcha.render(this.ref.current,
128
- {
129
- ...this.props,
130
- "error-callback" : this.handleError,
131
- "expired-callback" : this.handleExpire,
132
- "callback" : this.handleSubmit,
133
- });
146
+ const captchaId = hcaptcha.render(this.ref.current, renderParams);
134
147
 
135
- this.setState({ isRemoved: false, captchaId });
148
+ this.setState({ isRemoved: false, captchaId }, () => {
149
+ onReady && onReady();
150
+ });
136
151
  }
137
152
 
138
153
  resetCaptcha() {
139
- const { isApiReady, isRemoved, captchaId } = this.state;
154
+ const { captchaId } = this.state;
140
155
 
141
- if (!isApiReady || isRemoved) return
156
+ if (!this.isReady()) {
157
+ return;
158
+ }
142
159
  // Reset captcha state, removes stored token and unticks checkbox
143
160
  hcaptcha.reset(captchaId)
144
161
  }
145
162
 
146
163
  removeCaptcha(callback) {
147
- const { isApiReady, isRemoved, captchaId } = this.state;
164
+ const { captchaId } = this.state;
148
165
 
149
- if (!isApiReady || isRemoved) return
166
+ if (!this.isReady()) {
167
+ return;
168
+ }
150
169
 
151
170
  this.setState({ isRemoved: true }, () => {
152
171
  hcaptcha.remove(captchaId);
@@ -156,12 +175,13 @@ class HCaptcha extends React.Component {
156
175
 
157
176
  handleOnLoad () {
158
177
  this.setState({ isApiReady: true }, () => {
159
- // trigger onLoad if it exists
160
- const { onLoad } = this.props;
161
- if (onLoad) onLoad();
162
178
 
163
- // render captcha
164
- this.renderCaptcha();
179
+ // render captcha and wait for captcha id
180
+ this.renderCaptcha(() => {
181
+ // trigger onLoad if it exists
182
+ const { onLoad } = this.props;
183
+ if (onLoad) onLoad();
184
+ });
165
185
  });
166
186
  }
167
187
 
@@ -178,9 +198,12 @@ class HCaptcha extends React.Component {
178
198
 
179
199
  handleExpire () {
180
200
  const { onExpire } = this.props;
181
- const { isApiReady, isRemoved, captchaId } = this.state;
201
+ const { captchaId } = this.state;
202
+
203
+ if (!this.isReady()) {
204
+ return;
205
+ }
182
206
 
183
- if (!isApiReady || isRemoved) return
184
207
  hcaptcha.reset(captchaId) // If hCaptcha runs into error, reset captcha - hCaptcha
185
208
 
186
209
  if (onExpire) onExpire();
@@ -188,18 +211,52 @@ class HCaptcha extends React.Component {
188
211
 
189
212
  handleError (event) {
190
213
  const { onError } = this.props;
191
- const { isApiReady, isRemoved, captchaId } = this.state;
214
+ const { captchaId } = this.state;
192
215
 
193
- if (!isApiReady || isRemoved) return
216
+ if (!this.isReady()) {
217
+ return;
218
+ }
194
219
 
195
220
  hcaptcha.reset(captchaId) // If hCaptcha runs into error, reset captcha - hCaptcha
196
221
  if (onError) onError(event);
197
222
  }
198
223
 
224
+ isReady () {
225
+ const { isApiReady, isRemoved } = this.state;
226
+
227
+ return isApiReady && !isRemoved;
228
+ }
229
+
230
+ handleOpen () {
231
+ if (!this.isReady() || !this.props.onOpen) {
232
+ return;
233
+ }
234
+
235
+ this.props.onOpen();
236
+ }
237
+
238
+ handleClose () {
239
+ if (!this.isReady() || !this.props.onClose) {
240
+ return;
241
+ }
242
+
243
+ this.props.onClose();
244
+ }
245
+
246
+ handleChallengeExpired () {
247
+ if (!this.isReady() || !this.props.onChalExpired) {
248
+ return;
249
+ }
250
+
251
+ this.props.onChalExpired();
252
+ }
253
+
199
254
  execute (opts = null) {
200
- const { isApiReady, isRemoved, captchaId } = this.state;
255
+ const { captchaId } = this.state;
201
256
 
202
- if (!isApiReady || isRemoved) return;
257
+ if (!this.isReady()) {
258
+ return;
259
+ }
203
260
 
204
261
  if (opts && typeof opts !== "object") {
205
262
  opts = null;
package/types/index.d.ts CHANGED
@@ -15,6 +15,9 @@ interface HCaptchaState {
15
15
 
16
16
  interface HCaptchaProps {
17
17
  onExpire?: () => any;
18
+ onOpen?: () => any;
19
+ onClose?: () => any;
20
+ onChalExpired?: () => any;
18
21
  onError?: (event: string) => any;
19
22
  onVerify?: (token: string) => any;
20
23
  onLoad?: () => any;