@salesforcedevs/dw-components 1.3.397-canary.3 → 1.3.397-canary.5
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/lwc.config.json +1 -0
- package/package.json +1 -1
- package/src/modules/dw/deSignupForm/deSignupForm.html +5 -1
- package/src/modules/dw/deSignupForm/deSignupForm.ts +20 -26
- package/src/modules/dw/deSignupForm/submitForm.ts +3 -8
- package/src/modules/dw/recaptcha/recaptcha.css +14 -0
- package/src/modules/dw/recaptcha/recaptcha.html +12 -0
- package/src/modules/dw/recaptcha/recaptcha.ts +94 -0
package/lwc.config.json
CHANGED
package/package.json
CHANGED
|
@@ -117,7 +117,11 @@
|
|
|
117
117
|
<span lwc:dom="manual" class="legal-checkbox privacy-terms"></span>
|
|
118
118
|
</dx-checkbox>
|
|
119
119
|
|
|
120
|
-
<slot
|
|
120
|
+
<slot
|
|
121
|
+
class="form-captcha-slot"
|
|
122
|
+
name="form-captcha-slot"
|
|
123
|
+
onslotchange={onSlotChange}
|
|
124
|
+
></slot>
|
|
121
125
|
|
|
122
126
|
<dx-button
|
|
123
127
|
class="submit-button"
|
|
@@ -4,6 +4,7 @@ import { track as trackGtm } from "dxUtils/analytics";
|
|
|
4
4
|
import { submitForm, ErrorCode, SignupForm, UserData } from "./submitForm";
|
|
5
5
|
import { CountryOptions } from "typings/custom";
|
|
6
6
|
import { stateMap } from "dwUtils/forms";
|
|
7
|
+
import Recaptcha from "packages/@salesforcedevs/dw-components/src/modules/dw/recaptcha/recaptcha";
|
|
7
8
|
|
|
8
9
|
type SelectOption = { value: string; label: string };
|
|
9
10
|
type Country = SelectOption & {
|
|
@@ -47,7 +48,7 @@ export default class DeSignupForm extends LightningElement {
|
|
|
47
48
|
private marketingText: string = "";
|
|
48
49
|
private showLegalCheckbox: boolean = false;
|
|
49
50
|
private privacyText: string = "";
|
|
50
|
-
private
|
|
51
|
+
private recaptchaComponent: Recaptcha | null = null;
|
|
51
52
|
|
|
52
53
|
@track knownDuplicateUsers: string[] = [];
|
|
53
54
|
|
|
@@ -74,26 +75,6 @@ export default class DeSignupForm extends LightningElement {
|
|
|
74
75
|
return stateMap[this.country] || [];
|
|
75
76
|
}
|
|
76
77
|
|
|
77
|
-
@api
|
|
78
|
-
recaptchaCallback(responseToken: string) {
|
|
79
|
-
this.recaptchaToken = responseToken;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
@api
|
|
83
|
-
recaptchaExpiredCallback() {
|
|
84
|
-
this.recaptchaToken = "";
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
@api
|
|
88
|
-
recaptchaErrorCallback() {
|
|
89
|
-
this.resetRecaptcha();
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
private resetRecaptcha() {
|
|
93
|
-
this.recaptchaToken = "";
|
|
94
|
-
(window as any).grecaptcha?.reset();
|
|
95
|
-
}
|
|
96
|
-
|
|
97
78
|
connectedCallback(): void {
|
|
98
79
|
this.getCountryList();
|
|
99
80
|
}
|
|
@@ -104,6 +85,10 @@ export default class DeSignupForm extends LightningElement {
|
|
|
104
85
|
this.updateInnerHtml("span.legal-checkbox", this.privacyText);
|
|
105
86
|
}
|
|
106
87
|
|
|
88
|
+
onSlotChange(e: Event) {
|
|
89
|
+
this.recaptchaComponent = e.target as Recaptcha;
|
|
90
|
+
}
|
|
91
|
+
|
|
107
92
|
createUUID() {
|
|
108
93
|
if ("crypto" in window && "randomUUID" in crypto) {
|
|
109
94
|
return crypto.randomUUID();
|
|
@@ -169,7 +154,7 @@ export default class DeSignupForm extends LightningElement {
|
|
|
169
154
|
}
|
|
170
155
|
|
|
171
156
|
const formData = this.prepareUserData();
|
|
172
|
-
if (!formData
|
|
157
|
+
if (!formData) {
|
|
173
158
|
return;
|
|
174
159
|
}
|
|
175
160
|
|
|
@@ -188,7 +173,7 @@ export default class DeSignupForm extends LightningElement {
|
|
|
188
173
|
);
|
|
189
174
|
|
|
190
175
|
this.setSubmitButtonDisabled(true);
|
|
191
|
-
submitForm(formData
|
|
176
|
+
submitForm(formData)
|
|
192
177
|
.then(() => {
|
|
193
178
|
trackGtm(event.target!, "custEv_signUp", {
|
|
194
179
|
authentication_method: "email",
|
|
@@ -212,7 +197,7 @@ export default class DeSignupForm extends LightningElement {
|
|
|
212
197
|
"/developer-legacy/signup/account-verification";
|
|
213
198
|
} else {
|
|
214
199
|
this.setSubmitButtonDisabled(false);
|
|
215
|
-
this.
|
|
200
|
+
this.recaptchaComponent?.reset();
|
|
216
201
|
if (error.message === ErrorCode.DUPLICATE) {
|
|
217
202
|
this.knownDuplicateUsers.push(formData.user.username);
|
|
218
203
|
} else {
|
|
@@ -257,7 +242,15 @@ export default class DeSignupForm extends LightningElement {
|
|
|
257
242
|
validCheckboxes = validCheckboxes && checkbox.reportValidity();
|
|
258
243
|
});
|
|
259
244
|
|
|
260
|
-
|
|
245
|
+
const hasInvalidCaptcha =
|
|
246
|
+
this.recaptchaComponent &&
|
|
247
|
+
!this.recaptchaComponent.reportValidity();
|
|
248
|
+
|
|
249
|
+
if (
|
|
250
|
+
fields.some((field) => !field.valid) ||
|
|
251
|
+
!validCheckboxes ||
|
|
252
|
+
hasInvalidCaptcha
|
|
253
|
+
) {
|
|
261
254
|
return null;
|
|
262
255
|
}
|
|
263
256
|
|
|
@@ -277,7 +270,8 @@ export default class DeSignupForm extends LightningElement {
|
|
|
277
270
|
user: userData as UserData,
|
|
278
271
|
default_driver: DEFAULT_DRIVER,
|
|
279
272
|
info_email: InfoEmail,
|
|
280
|
-
form_join_id_unique: this.createUUID()
|
|
273
|
+
form_join_id_unique: this.createUUID(),
|
|
274
|
+
"g-recaptcha-response": this.recaptchaComponent?.value
|
|
281
275
|
};
|
|
282
276
|
|
|
283
277
|
return formData;
|
|
@@ -24,6 +24,7 @@ export type SignupForm = {
|
|
|
24
24
|
default_driver: string;
|
|
25
25
|
info_email: "True" | "False";
|
|
26
26
|
form_join_id_unique: string;
|
|
27
|
+
"g-recaptcha-response"?: string;
|
|
27
28
|
};
|
|
28
29
|
|
|
29
30
|
type OrgStatus = "New" | "In Progress" | "Error";
|
|
@@ -79,19 +80,13 @@ const checkOrgStatus = (id: string, retries = 0): Promise<void> => {
|
|
|
79
80
|
});
|
|
80
81
|
};
|
|
81
82
|
|
|
82
|
-
export const submitForm = async (
|
|
83
|
-
formData: SignupForm,
|
|
84
|
-
recaptchaToken: string
|
|
85
|
-
): Promise<void> => {
|
|
83
|
+
export const submitForm = async (formData: SignupForm): Promise<void> => {
|
|
86
84
|
const response = await fetch(CREATE_PATH, {
|
|
87
85
|
method: "POST",
|
|
88
86
|
headers: {
|
|
89
87
|
"Content-Type": "application/json"
|
|
90
88
|
},
|
|
91
|
-
body: JSON.stringify(
|
|
92
|
-
...formData,
|
|
93
|
-
"g-recaptcha-response": recaptchaToken
|
|
94
|
-
})
|
|
89
|
+
body: JSON.stringify(formData)
|
|
95
90
|
});
|
|
96
91
|
|
|
97
92
|
if (!response.ok) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
@import "dxHelpers/reset";
|
|
2
|
+
@import "dxHelpers/text";
|
|
3
|
+
|
|
4
|
+
.container {
|
|
5
|
+
width: 100%;
|
|
6
|
+
display: flex;
|
|
7
|
+
justify-content: center;
|
|
8
|
+
margin-bottom: var(--dx-g-spacing-lg);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.error-message-box {
|
|
12
|
+
margin-top: var(--dx-g-spacing-xs);
|
|
13
|
+
display: block;
|
|
14
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { api, LightningElement } from "lwc";
|
|
2
|
+
|
|
3
|
+
// NOTE: This component assumes it is the only Recaptcha widget on a page.
|
|
4
|
+
type RecaptchaApi = {
|
|
5
|
+
render(element: HTMLElement | null, recaptchaConfig: any): void;
|
|
6
|
+
reset(): void;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default class Recaptcha extends LightningElement {
|
|
10
|
+
private errorMessage = "";
|
|
11
|
+
private recaptchaDiv!: HTMLDivElement;
|
|
12
|
+
private recaptchaApi?: RecaptchaApi;
|
|
13
|
+
private recaptchaToken = "";
|
|
14
|
+
|
|
15
|
+
@api missingErrorMessage = "Complete the captcha";
|
|
16
|
+
@api recaptchaSiteKey = "";
|
|
17
|
+
|
|
18
|
+
@api // read only
|
|
19
|
+
get value() {
|
|
20
|
+
return this.recaptchaToken;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@api
|
|
24
|
+
recaptchaScriptOnLoad = (recaptchaApi: RecaptchaApi) => {
|
|
25
|
+
this.recaptchaApi = recaptchaApi;
|
|
26
|
+
if (this.recaptchaDiv) {
|
|
27
|
+
this.renderRecaptcha();
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
@api
|
|
32
|
+
reset = () => this.resetRecaptcha();
|
|
33
|
+
|
|
34
|
+
@api
|
|
35
|
+
reportValidity = () => {
|
|
36
|
+
if (!this.checkValidity()) {
|
|
37
|
+
this.errorMessage = this.missingErrorMessage;
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.errorMessage = "";
|
|
42
|
+
return true;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
@api
|
|
46
|
+
checkValidity = () => {
|
|
47
|
+
return Boolean(this.recaptchaToken);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
renderedCallback() {
|
|
51
|
+
if (!this.recaptchaDiv) {
|
|
52
|
+
this.recaptchaDiv = this.template.querySelector(
|
|
53
|
+
".g-recaptcha"
|
|
54
|
+
) as HTMLDivElement;
|
|
55
|
+
if (this.recaptchaApi) {
|
|
56
|
+
this.renderRecaptcha();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private renderRecaptcha = () => {
|
|
62
|
+
const smallMatchMedia = window.matchMedia(
|
|
63
|
+
"screen and (max-width: 768px)"
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// render recaptcha, referencing this component's callbacks for handling events
|
|
67
|
+
this.recaptchaApi?.render(this.recaptchaDiv, {
|
|
68
|
+
sitekey: this.recaptchaSiteKey,
|
|
69
|
+
callback: this.recaptchaCallback,
|
|
70
|
+
"expired-callback": this.recaptchaExpiredCallback,
|
|
71
|
+
"error-callback": this.recaptchaErrorCallback,
|
|
72
|
+
size: smallMatchMedia.matches ? "compact" : "normal"
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
private recaptchaCallback = (token: string) => {
|
|
77
|
+
this.recaptchaToken = token;
|
|
78
|
+
this.errorMessage = "";
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
private recaptchaExpiredCallback = () => {
|
|
82
|
+
this.recaptchaToken = ""; // note: recaptcha element itself handles showing an "expired" message
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
private recaptchaErrorCallback = () => {
|
|
86
|
+
this.resetRecaptcha();
|
|
87
|
+
this.errorMessage = ""; // this error message is only set to a value when the form is submitted or `reportValidity` is called explicitly
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
private resetRecaptcha = () => {
|
|
91
|
+
this.recaptchaToken = "";
|
|
92
|
+
this.recaptchaApi?.reset();
|
|
93
|
+
};
|
|
94
|
+
}
|