@salesforcedevs/dw-components 1.3.148-canary.2 → 1.3.148-canary.21
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 +1 -1
- package/src/modules/dw/j2sCardSequence/j2sCardSequence.css +7 -1
- package/src/modules/dw/j2sCardSequence/j2sCardSequence.html +8 -3
- package/src/modules/dw/j2sCardSequence/j2sCardSequence.ts +130 -56
- package/src/modules/dw/j2sCardSequence/mockProps.ts +58 -43
- package/src/modules/dw/j2sForm/j2sForm.css +12 -2
- package/src/modules/dw/j2sForm/j2sForm.html +21 -21
- package/src/modules/dw/j2sForm/j2sForm.ts +78 -74
package/package.json
CHANGED
|
@@ -40,7 +40,13 @@ dx-step-sequence-step:last-child dx-card-step::part(footer-container) {
|
|
|
40
40
|
|
|
41
41
|
.body-markup {
|
|
42
42
|
display: block;
|
|
43
|
-
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.final-email-address {
|
|
46
|
+
display: block;
|
|
47
|
+
margin-top: 20px;
|
|
48
|
+
line-height: 24px;
|
|
49
|
+
color: var(--dx-g-blue-vibrant-50);
|
|
44
50
|
}
|
|
45
51
|
|
|
46
52
|
.referral-email-field {
|
|
@@ -35,11 +35,15 @@
|
|
|
35
35
|
<dw-j2s-form
|
|
36
36
|
id={stepDatum.formData.formId}
|
|
37
37
|
countries={countries}
|
|
38
|
-
|
|
39
|
-
form-metadata={stepDatum.formMetadata}
|
|
38
|
+
form-metadata={stepDatum.formData}
|
|
40
39
|
incoming-user-info={formIncomingUserInfo}
|
|
41
40
|
onformvaliditychange={handleFormValidityChange}
|
|
42
|
-
|
|
41
|
+
recaptcha-token={recaptchaToken}
|
|
42
|
+
>
|
|
43
|
+
<div slot="form-captcha-slot">
|
|
44
|
+
<slot name="captcha-slot"></slot>
|
|
45
|
+
</div>
|
|
46
|
+
</dw-j2s-form>
|
|
43
47
|
</template>
|
|
44
48
|
</template>
|
|
45
49
|
</div>
|
|
@@ -59,6 +63,7 @@
|
|
|
59
63
|
loading={isLoading}
|
|
60
64
|
onclick={primaryActionHandler}
|
|
61
65
|
size="large"
|
|
66
|
+
target={primaryActionLinkTarget}
|
|
62
67
|
variant={primaryActionVariant}
|
|
63
68
|
>
|
|
64
69
|
{stepDatum.primaryAction.text}
|
|
@@ -3,6 +3,7 @@ import StepSequence from "dx/stepSequence";
|
|
|
3
3
|
import { getListWithUuids } from "dxUtils/list";
|
|
4
4
|
import { track } from "dxUtils/analytics";
|
|
5
5
|
import { toJson } from "dxUtils/normalizers";
|
|
6
|
+
import J2sForm from "dw/j2sForm";
|
|
6
7
|
|
|
7
8
|
export type StepDatum = {
|
|
8
9
|
bodyHtml?: string;
|
|
@@ -28,6 +29,7 @@ export type StepDatum = {
|
|
|
28
29
|
iconSprite?: string;
|
|
29
30
|
iconSymbol?: string;
|
|
30
31
|
link?: string;
|
|
32
|
+
linkTarget?: string;
|
|
31
33
|
text: string;
|
|
32
34
|
variant?: string;
|
|
33
35
|
};
|
|
@@ -36,22 +38,16 @@ export type StepDatum = {
|
|
|
36
38
|
stepTitle: string;
|
|
37
39
|
};
|
|
38
40
|
|
|
39
|
-
function isNoAnimationCookieSet() {
|
|
40
|
-
return document.cookie
|
|
41
|
-
.split(";")
|
|
42
|
-
.some((ck) => ck.trim() === "no_j2s_animation=true");
|
|
43
|
-
}
|
|
44
|
-
|
|
45
41
|
export default class J2sCardSequence extends LightningElement {
|
|
46
42
|
_didRenderStaticInnerHtml = false;
|
|
47
43
|
_isDrmLoading = false;
|
|
48
44
|
_isFormSubmitting = false;
|
|
49
45
|
_isJ2sFormInvalid = false;
|
|
50
46
|
_isTbidLoading = false;
|
|
47
|
+
_recaptchaResetTimeoutId: number | null = null;
|
|
51
48
|
_stepsData: StepDatum[] = [];
|
|
52
49
|
@api animateTransitions = false;
|
|
53
50
|
@api drmDomain: string = "https://drm.my.salesforce-sites.com";
|
|
54
|
-
@api drmRecaptchaSiteKey: string = "";
|
|
55
51
|
@api formHandler: string =
|
|
56
52
|
"https://formhandlermule-1s9xrb.5sc6y6-4.usa-e2.cloudhub.io/form_handler";
|
|
57
53
|
@api tbidApiBaseUrl: string | undefined;
|
|
@@ -63,34 +59,28 @@ export default class J2sCardSequence extends LightningElement {
|
|
|
63
59
|
}
|
|
64
60
|
set stepsData(value) {
|
|
65
61
|
// normalize incoming value with uuid keys and non-required props (for cleaner HTML template), etc.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
iconSymbol
|
|
77
|
-
stepDatum.primaryAction.iconSymbol || "arrow-right"
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
if (normalizedStepDatum.formData) {
|
|
82
|
-
(normalizedStepDatum as any).formMetadata = JSON.stringify(
|
|
83
|
-
normalizedStepDatum.formData
|
|
84
|
-
);
|
|
62
|
+
const valueWithUuids = getListWithUuids<StepDatum>(toJson(value));
|
|
63
|
+
this._stepsData = valueWithUuids.map((stepDatum) => {
|
|
64
|
+
const normalizedStepDatum = {
|
|
65
|
+
...stepDatum,
|
|
66
|
+
headerData: stepDatum.headerData || {},
|
|
67
|
+
primaryAction: {
|
|
68
|
+
...stepDatum.primaryAction,
|
|
69
|
+
variant: stepDatum.primaryAction.variant || "primary",
|
|
70
|
+
iconSprite: stepDatum.primaryAction.iconSprite || "general",
|
|
71
|
+
iconSymbol:
|
|
72
|
+
stepDatum.primaryAction.iconSymbol || "arrow-right"
|
|
85
73
|
}
|
|
74
|
+
};
|
|
86
75
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
);
|
|
76
|
+
return normalizedStepDatum;
|
|
77
|
+
});
|
|
90
78
|
}
|
|
91
79
|
|
|
92
80
|
private countries = "[]";
|
|
93
81
|
private currentSequenceStep = 0;
|
|
82
|
+
private didFormSubmissionFail = false;
|
|
83
|
+
private recaptchaToken = "";
|
|
94
84
|
private sessionStorageId = "j2s-card-sequence";
|
|
95
85
|
private tbidUserInfo: any; // TODO typings?
|
|
96
86
|
|
|
@@ -119,19 +109,32 @@ export default class J2sCardSequence extends LightningElement {
|
|
|
119
109
|
.variant;
|
|
120
110
|
}
|
|
121
111
|
|
|
112
|
+
private get primaryActionLinkTarget() {
|
|
113
|
+
return (
|
|
114
|
+
this.stepsData[this.currentSequenceStep || 0].primaryAction
|
|
115
|
+
.linkTarget || "_blank"
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
122
119
|
private get formIncomingUserInfo() {
|
|
123
120
|
if (!this.tbidUserInfo) {
|
|
124
|
-
return
|
|
121
|
+
return {};
|
|
125
122
|
}
|
|
126
123
|
|
|
127
|
-
return
|
|
128
|
-
firstName: this.tbidUserInfo.given_name,
|
|
129
|
-
lastName: this.tbidUserInfo.family_name,
|
|
130
|
-
email: this.tbidUserInfo.email,
|
|
131
|
-
jobRole:
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
124
|
+
return {
|
|
125
|
+
firstName: decodeURIComponent(this.tbidUserInfo.given_name),
|
|
126
|
+
lastName: decodeURIComponent(this.tbidUserInfo.family_name),
|
|
127
|
+
email: decodeURIComponent(this.tbidUserInfo.email),
|
|
128
|
+
jobRole: decodeURIComponent(
|
|
129
|
+
this.tbidUserInfo.custom_attributes?.Role
|
|
130
|
+
),
|
|
131
|
+
companyName: decodeURIComponent(
|
|
132
|
+
this.tbidUserInfo.custom_attributes?.CompanyName
|
|
133
|
+
),
|
|
134
|
+
country: decodeURIComponent(
|
|
135
|
+
this.tbidUserInfo.custom_attributes?.Country
|
|
136
|
+
)
|
|
137
|
+
};
|
|
135
138
|
}
|
|
136
139
|
|
|
137
140
|
private get initialStepIndex() {
|
|
@@ -158,20 +161,29 @@ export default class J2sCardSequence extends LightningElement {
|
|
|
158
161
|
);
|
|
159
162
|
}
|
|
160
163
|
|
|
164
|
+
@api
|
|
165
|
+
recaptchaCallback(responseToken: string) {
|
|
166
|
+
this.recaptchaToken = responseToken;
|
|
167
|
+
clearTimeout(this._recaptchaResetTimeoutId as number);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@api
|
|
171
|
+
recaptchaExpiredCallback() {
|
|
172
|
+
this.recaptchaToken = "";
|
|
173
|
+
clearTimeout(this._recaptchaResetTimeoutId as number);
|
|
174
|
+
}
|
|
175
|
+
|
|
161
176
|
connectedCallback() {
|
|
162
177
|
this.getCountryList();
|
|
163
178
|
this.getTbidUserInfo();
|
|
164
179
|
// This has to be defined here since it depends on a variable that depends on sessionStorage.
|
|
165
180
|
this.currentSequenceStep = this.initialStepIndex;
|
|
166
181
|
this.resetCustomButtonColors();
|
|
167
|
-
if (isNoAnimationCookieSet()) {
|
|
168
|
-
/* eslint-disable-next-line @lwc/lwc/no-api-reassignments */
|
|
169
|
-
this.animateTransitions = false;
|
|
170
|
-
}
|
|
171
182
|
}
|
|
172
183
|
|
|
173
184
|
renderedCallback() {
|
|
174
185
|
if (this.isLoading) {
|
|
186
|
+
this._didRenderStaticInnerHtml = false; // only when `this.isLoading` is true will static HTML need to be re-rendered, since the content will have been replaced by the spinner
|
|
175
187
|
return;
|
|
176
188
|
}
|
|
177
189
|
|
|
@@ -179,6 +191,14 @@ export default class J2sCardSequence extends LightningElement {
|
|
|
179
191
|
this.updateStaticInnerHtml();
|
|
180
192
|
this._didRenderStaticInnerHtml = true;
|
|
181
193
|
}
|
|
194
|
+
|
|
195
|
+
if (this.didFormSubmissionFail) {
|
|
196
|
+
const j2sFormEl = (this.template.querySelector(
|
|
197
|
+
"dw-j2s-form"
|
|
198
|
+
) as unknown) as J2sForm | null;
|
|
199
|
+
j2sFormEl?.notifySubmitFailure();
|
|
200
|
+
this.didFormSubmissionFail = false;
|
|
201
|
+
}
|
|
182
202
|
}
|
|
183
203
|
|
|
184
204
|
handleFormValidityChange({ detail }: CustomEvent) {
|
|
@@ -188,6 +208,21 @@ export default class J2sCardSequence extends LightningElement {
|
|
|
188
208
|
handleStepSequenceChange({ detail }: CustomEvent) {
|
|
189
209
|
this.currentSequenceStep = detail.stepIndex;
|
|
190
210
|
this.resetCustomButtonColors();
|
|
211
|
+
|
|
212
|
+
if (
|
|
213
|
+
this.currentSequenceStep !== 1 &&
|
|
214
|
+
this.recaptchaToken &&
|
|
215
|
+
this._recaptchaResetTimeoutId === null
|
|
216
|
+
) {
|
|
217
|
+
// If the recaptcha element expires on its own while you are not on the 2nd (form) step, it messes with the
|
|
218
|
+
// page's layout (basically breaks it). So, we need to reset it _before_ expiration if you leave that step.
|
|
219
|
+
// We do it with a timeout so that the token is not immediately wiped if the user goes back and forth a bit.
|
|
220
|
+
this._recaptchaResetTimeoutId = window.setTimeout(() => {
|
|
221
|
+
this._recaptchaResetTimeoutId = null;
|
|
222
|
+
this.recaptchaToken = "";
|
|
223
|
+
(window as any).grecaptcha.reset();
|
|
224
|
+
}, 1000 * 30 /* 30 seconds */);
|
|
225
|
+
}
|
|
191
226
|
}
|
|
192
227
|
|
|
193
228
|
handleGetStarted(evt: Event) {
|
|
@@ -198,13 +233,12 @@ export default class J2sCardSequence extends LightningElement {
|
|
|
198
233
|
evt.preventDefault();
|
|
199
234
|
this.initialStepIndex = 1;
|
|
200
235
|
this._isTbidLoading = true;
|
|
201
|
-
//
|
|
202
|
-
window.__startTbidLogin();
|
|
236
|
+
(window as any).__startTbidLogin?.(); // call out to DSC tbid controller
|
|
203
237
|
}
|
|
204
238
|
}
|
|
205
239
|
|
|
206
|
-
handleFormSubmit(evt: Event) {
|
|
207
|
-
if (!this.formHandler || this.isJ2sFormInvalid) {
|
|
240
|
+
async handleFormSubmit(evt: Event) {
|
|
241
|
+
if (!this.formHandler || this.isJ2sFormInvalid || !this.isLoggedIn) {
|
|
208
242
|
return;
|
|
209
243
|
}
|
|
210
244
|
|
|
@@ -212,8 +246,10 @@ export default class J2sCardSequence extends LightningElement {
|
|
|
212
246
|
this._isFormSubmitting = true;
|
|
213
247
|
|
|
214
248
|
try {
|
|
215
|
-
const
|
|
216
|
-
|
|
249
|
+
const j2sFormEl = (this.template.querySelector(
|
|
250
|
+
"dw-j2s-form"
|
|
251
|
+
) as unknown) as J2sForm | null;
|
|
252
|
+
const formData = j2sFormEl?.formDataForDrm;
|
|
217
253
|
// For some countries, explicit approvals will be present; for others, it is implicitly assumed
|
|
218
254
|
if (typeof formData.Opt_In__c !== "boolean") {
|
|
219
255
|
formData.Opt_In__c = true;
|
|
@@ -221,10 +257,38 @@ export default class J2sCardSequence extends LightningElement {
|
|
|
221
257
|
if (typeof formData.Privacy_Statement__c !== "boolean") {
|
|
222
258
|
formData.Privacy_Statement__c = true;
|
|
223
259
|
}
|
|
224
|
-
|
|
225
|
-
this.
|
|
260
|
+
|
|
261
|
+
const res = await fetch(this.formHandler, {
|
|
262
|
+
method: "POST",
|
|
263
|
+
headers: {
|
|
264
|
+
"Captcha-Response": this.recaptchaToken
|
|
265
|
+
},
|
|
266
|
+
body: JSON.stringify({
|
|
267
|
+
...formData,
|
|
268
|
+
handlerName: "J2S Registration Page",
|
|
269
|
+
TBID__c: this.tbidUserInfo.user_id
|
|
270
|
+
})
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
if (res.ok) {
|
|
274
|
+
const resData = await res.json();
|
|
275
|
+
const resOutput = resData.handlePostRequestOutput || resData;
|
|
276
|
+
if (resOutput?.status === "success") {
|
|
277
|
+
this.jumpToStepIndex(2, false);
|
|
278
|
+
this.initialStepIndex = 0; // don't save final step in storage; rather, reset the starting step
|
|
279
|
+
} else {
|
|
280
|
+
throw new Error(
|
|
281
|
+
`Form submission failed. (status: ${resOutput.status}, message: ${resOutput.errorMessage})`
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
} else {
|
|
285
|
+
throw new Error(
|
|
286
|
+
`Form submission failed. Non-ok response received from DRM.`
|
|
287
|
+
);
|
|
288
|
+
}
|
|
226
289
|
} catch (ex) {
|
|
227
|
-
|
|
290
|
+
this.didFormSubmissionFail = true;
|
|
291
|
+
console.error(ex);
|
|
228
292
|
}
|
|
229
293
|
|
|
230
294
|
this._isFormSubmitting = false;
|
|
@@ -281,14 +345,15 @@ export default class J2sCardSequence extends LightningElement {
|
|
|
281
345
|
);
|
|
282
346
|
}
|
|
283
347
|
|
|
284
|
-
private jumpToStepIndex(stepIndex: number) {
|
|
285
|
-
|
|
348
|
+
private jumpToStepIndex(stepIndex: number, andStoreIndex = true) {
|
|
349
|
+
if (andStoreIndex) {
|
|
350
|
+
this.initialStepIndex = stepIndex; // update sessionStorage value
|
|
351
|
+
}
|
|
286
352
|
((this.template.querySelector(
|
|
287
353
|
"dx-step-sequence"
|
|
288
354
|
) as unknown) as StepSequence)?.jumpToStep(stepIndex);
|
|
289
355
|
}
|
|
290
356
|
|
|
291
|
-
// HTML is received via config from DSC and should be static after initial render.
|
|
292
357
|
private updateStaticInnerHtml() {
|
|
293
358
|
const bodyMarkupContainers = this.template.querySelectorAll(
|
|
294
359
|
"span.body-markup"
|
|
@@ -304,7 +369,16 @@ export default class J2sCardSequence extends LightningElement {
|
|
|
304
369
|
secondaryActionMarkupContainers[stepIndex];
|
|
305
370
|
|
|
306
371
|
if (bodyMarkupContainer) {
|
|
307
|
-
|
|
372
|
+
let markup = bodyHtml;
|
|
373
|
+
if (stepIndex === 2) {
|
|
374
|
+
// User's email is supposed to show on final step
|
|
375
|
+
markup = `${bodyHtml}${
|
|
376
|
+
!this.formIncomingUserInfo.email
|
|
377
|
+
? ""
|
|
378
|
+
: `<span class="final-email-address">${this.formIncomingUserInfo.email}</span>`
|
|
379
|
+
}`;
|
|
380
|
+
}
|
|
381
|
+
bodyMarkupContainer.innerHTML = markup || "";
|
|
308
382
|
}
|
|
309
383
|
if (secondaryActionMarkupContainer) {
|
|
310
384
|
secondaryActionMarkupContainer.innerHTML =
|
|
@@ -316,7 +390,7 @@ export default class J2sCardSequence extends LightningElement {
|
|
|
316
390
|
private async getCountryList() {
|
|
317
391
|
this._isDrmLoading = true;
|
|
318
392
|
|
|
319
|
-
const errorMsg = "Error while getting
|
|
393
|
+
const errorMsg = "Error while getting country list";
|
|
320
394
|
try {
|
|
321
395
|
const res = await fetch(
|
|
322
396
|
`${this.drmDomain}/utilities/services/apexrest/countries`
|
|
@@ -1,46 +1,61 @@
|
|
|
1
|
-
export const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
{
|
|
10
|
-
showStepNumber: "true",
|
|
11
|
-
stepTitle: "Complete your registration.",
|
|
12
|
-
formData: {
|
|
13
|
-
countriesWithReferralInput: ["IN"],
|
|
14
|
-
countriewWithRequiredJobRole: ["CA"],
|
|
15
|
-
notAvailableAlertInfo: {
|
|
16
|
-
title: "Not Available",
|
|
17
|
-
body: 'The Journey to Salesforce program is not currently available in your country. See the <a href="https://www.google.com/">program FAQs</a> for a list of participating countries.'
|
|
1
|
+
export const stepSequenceData = {
|
|
2
|
+
animateTransitions: "true",
|
|
3
|
+
stepsData: [
|
|
4
|
+
{
|
|
5
|
+
bodyHtml:
|
|
6
|
+
'<p class="dx-text-display-7">Register for Journey to Salesforce and you\'ll be on your way to becoming a Salesforce Developer.</p><ul class="dx-text-display-8"><li>Step 1: Log in or sign up for a free Trailblazer.me account</li><li>Step 2: Confirm your program registration</li><li>Step 3: Begin your journey!</li></ul>',
|
|
7
|
+
primaryAction: {
|
|
8
|
+
text: "Get Started"
|
|
18
9
|
},
|
|
19
|
-
|
|
10
|
+
showStepNumber: "true",
|
|
11
|
+
stepTitle: "Get started now."
|
|
20
12
|
},
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
13
|
+
{
|
|
14
|
+
bodyHtml:
|
|
15
|
+
'<p class="dx-text-display-7">You\'re almost done! Please review your information and agree to the Journey to Salesforce program terms and conditions.</p>',
|
|
16
|
+
formData: {
|
|
17
|
+
countriesWithReferralInput: ["IN"],
|
|
18
|
+
countriesWithRequiredJobRole: ["CA"],
|
|
19
|
+
formId: "j2s-form",
|
|
20
|
+
j2sTerms:
|
|
21
|
+
'I agree to the Journey to Salesforce Program Terms, subject to the <a href="http://www.google.com">Salesforce Program Agreement</a>.',
|
|
22
|
+
notAvailableAlertInfo: {
|
|
23
|
+
title: "Not Available",
|
|
24
|
+
body:
|
|
25
|
+
'The Journey to Salesforce program is not currently available in your country. See the <a href="https://www.google.com/">program FAQs</a> for a list of participating countries.'
|
|
26
|
+
},
|
|
27
|
+
participatingCountryCodes: ["US", "CA", "IN"]
|
|
28
|
+
},
|
|
29
|
+
primaryAction: {
|
|
30
|
+
text: "Complete Registration"
|
|
31
|
+
},
|
|
32
|
+
secondaryActionHtml:
|
|
33
|
+
'<span class="required-notice">* Required to participate in Journey to Salesforce</span>',
|
|
34
|
+
showStepNumber: "true",
|
|
35
|
+
stepTitle: "Complete your registration."
|
|
36
36
|
},
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
37
|
+
{
|
|
38
|
+
bodyHtml:
|
|
39
|
+
'<div class="dx-text-display-8"><p>Welcome to Journey to Salesforce! Congratulations on taking this step in your journey towards becoming a Salesforce Developer. Through this program you\'ll gain the knowledge, experience, and skills to help you excel in the Salesforce ecosystem.</p><p>To get started, click the button below to join the Journey to Salesforce community, and make sure to check your email for links to bookmark!</p></div>',
|
|
40
|
+
headerData: {
|
|
41
|
+
backgroundColor: "green-vibrant-95",
|
|
42
|
+
iconSymbol: "check-circle",
|
|
43
|
+
iconSprite: "general",
|
|
44
|
+
iconColor: "green-vibrant-65",
|
|
45
|
+
textColor: "indigo-vibrant-20"
|
|
46
|
+
},
|
|
47
|
+
primaryAction: {
|
|
48
|
+
iconSprite: "utility",
|
|
49
|
+
iconSymbol: "new_window",
|
|
50
|
+
link:
|
|
51
|
+
"https://salesforce-external.slack.com/archives/C05HJK4PGJV",
|
|
52
|
+
text: "Join Slack",
|
|
53
|
+
variant: "custom"
|
|
54
|
+
},
|
|
55
|
+
secondaryActionHtml:
|
|
56
|
+
'<span class="dx-text-display-7">Ready to jump in?</span>',
|
|
57
|
+
showStepNumber: "true",
|
|
58
|
+
stepTitle: "Your Registration is Confirmed"
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
};
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
.form-row .terms {
|
|
24
|
-
--dx-c-checkbox-text-line-height:
|
|
25
|
-
--dx-c-checkbox-font-size:
|
|
24
|
+
--dx-c-checkbox-text-line-height: var(--dx-g-text-lg);
|
|
25
|
+
--dx-c-checkbox-font-size: var(--dx-g-text-sm);
|
|
26
26
|
|
|
27
27
|
font-family: var(--dx-g-font-sans);
|
|
28
28
|
font-size: var(--dx-c-checkbox-font-size);
|
|
@@ -44,3 +44,13 @@
|
|
|
44
44
|
.form-terms-container {
|
|
45
45
|
padding-top: var(--dx-g-spacing-sm);
|
|
46
46
|
}
|
|
47
|
+
|
|
48
|
+
.form-captcha-slot {
|
|
49
|
+
align-items: center;
|
|
50
|
+
display: flex;
|
|
51
|
+
justify-content: center;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
dx-input[disabled] {
|
|
55
|
+
--dx-c-input-background: revert;
|
|
56
|
+
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
placeholder="Your first name"
|
|
11
11
|
required
|
|
12
12
|
validity-on-blur
|
|
13
|
-
|
|
13
|
+
onchange={handleFormFieldChange}
|
|
14
14
|
value={state.firstName}
|
|
15
15
|
></dx-input>
|
|
16
16
|
<dx-input
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
placeholder="Your last name"
|
|
22
22
|
required
|
|
23
23
|
validity-on-blur
|
|
24
|
-
|
|
24
|
+
onchange={handleFormFieldChange}
|
|
25
25
|
value={state.lastName}
|
|
26
26
|
></dx-input>
|
|
27
27
|
</div>
|
|
@@ -34,21 +34,10 @@
|
|
|
34
34
|
placeholder="Your company name"
|
|
35
35
|
required
|
|
36
36
|
validity-on-blur
|
|
37
|
-
onblur={handleRequiredFieldUpdate}
|
|
38
37
|
value={state.companyName}
|
|
38
|
+
onchange={handleFormFieldChange}
|
|
39
39
|
></dx-input>
|
|
40
40
|
<dx-select
|
|
41
|
-
if:false={isJobRoleRequired}
|
|
42
|
-
class="job-role-field"
|
|
43
|
-
label="Job Role"
|
|
44
|
-
message-when-value-missing="Select your job role"
|
|
45
|
-
name="DRM_Role__c"
|
|
46
|
-
options={jobRoles}
|
|
47
|
-
placeholder="Select Job Role"
|
|
48
|
-
value={state.jobRole}
|
|
49
|
-
></dx-select>
|
|
50
|
-
<dx-select
|
|
51
|
-
if:true={isJobRoleRequired}
|
|
52
41
|
class="job-role-field"
|
|
53
42
|
label="Job Role"
|
|
54
43
|
message-when-value-missing="Select your job role"
|
|
@@ -57,7 +46,7 @@
|
|
|
57
46
|
placeholder="Select Job Role"
|
|
58
47
|
required
|
|
59
48
|
value={state.jobRole}
|
|
60
|
-
onchange={
|
|
49
|
+
onchange={handleFormFieldChange}
|
|
61
50
|
></dx-select>
|
|
62
51
|
</div>
|
|
63
52
|
<div class="form-row">
|
|
@@ -68,8 +57,9 @@
|
|
|
68
57
|
label="Email"
|
|
69
58
|
missing-error-message="Enter your email address"
|
|
70
59
|
validity-on-blur="true"
|
|
60
|
+
disabled
|
|
61
|
+
readonly="true"
|
|
71
62
|
required
|
|
72
|
-
onblur={handleRequiredFieldUpdate}
|
|
73
63
|
value={state.email}
|
|
74
64
|
></dx-input>
|
|
75
65
|
<dx-select
|
|
@@ -80,7 +70,7 @@
|
|
|
80
70
|
placeholder="Select Country/Region"
|
|
81
71
|
required
|
|
82
72
|
value={state.country}
|
|
83
|
-
onchange={
|
|
73
|
+
onchange={handleFormFieldChange}
|
|
84
74
|
></dx-select>
|
|
85
75
|
</div>
|
|
86
76
|
<div class="form-row" if:true={isCountryWithReferralInput}>
|
|
@@ -88,7 +78,9 @@
|
|
|
88
78
|
name="Referral_Email__c"
|
|
89
79
|
class="referral-email-field full-width"
|
|
90
80
|
label="If anyone has referred you, please share their Email ID below, with their consent."
|
|
81
|
+
validity-on-blur="true"
|
|
91
82
|
type="email"
|
|
83
|
+
onchange={handleFormFieldChange}
|
|
92
84
|
></dx-input>
|
|
93
85
|
</div>
|
|
94
86
|
<div class="form-row" if:true={isNotParticipatingCountry}>
|
|
@@ -99,6 +91,14 @@
|
|
|
99
91
|
variant="accented"
|
|
100
92
|
></dx-alert>
|
|
101
93
|
</div>
|
|
94
|
+
<div class="form-row" if:true={showSubmitFailureAlert}>
|
|
95
|
+
<dx-alert
|
|
96
|
+
alert-info={formSubmitFailureAlertInfo}
|
|
97
|
+
class="full-width"
|
|
98
|
+
type="danger"
|
|
99
|
+
variant="accented"
|
|
100
|
+
></dx-alert>
|
|
101
|
+
</div>
|
|
102
102
|
<div class="form-terms-container">
|
|
103
103
|
<div class="form-row" if:true={state.showMarketingText}>
|
|
104
104
|
<dx-checkbox
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
name="Opt_In__c"
|
|
107
107
|
required
|
|
108
108
|
class="terms"
|
|
109
|
-
onchange={
|
|
109
|
+
onchange={handleFormFieldChange}
|
|
110
110
|
disabled={isNotParticipatingCountry}
|
|
111
111
|
>
|
|
112
112
|
<span lwc:dom="manual" class="marketing-terms"></span>
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
name="j2s_terms"
|
|
119
119
|
required
|
|
120
120
|
class="terms"
|
|
121
|
-
onchange={
|
|
121
|
+
onchange={handleFormFieldChange}
|
|
122
122
|
disabled={isNotParticipatingCountry}
|
|
123
123
|
>
|
|
124
124
|
<span lwc:dom="manual" class="j2s-terms"></span>
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
name="Privacy_Statement__c"
|
|
132
132
|
required
|
|
133
133
|
class="terms"
|
|
134
|
-
onchange={
|
|
134
|
+
onchange={handleFormFieldChange}
|
|
135
135
|
disabled={isNotParticipatingCountry}
|
|
136
136
|
>
|
|
137
137
|
<span lwc:dom="manual" class="legal-terms"></span>
|
|
@@ -154,7 +154,7 @@
|
|
|
154
154
|
class="legal-terms no-checkbox"
|
|
155
155
|
></span>
|
|
156
156
|
</div>
|
|
157
|
-
<
|
|
157
|
+
<slot class="form-captcha-slot" name="form-captcha-slot"></slot>
|
|
158
158
|
</div>
|
|
159
159
|
</form>
|
|
160
160
|
</template>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { toJson } from "dxUtils/normalizers";
|
|
2
2
|
import { api } from "lwc";
|
|
3
|
+
import debounce from "debounce";
|
|
3
4
|
import { LightningElementWithState } from "dxBaseElements/lightningElementWithState";
|
|
4
5
|
|
|
5
6
|
// Hard-coding for now because we can't get information about an endpoint for this
|
|
@@ -33,15 +34,13 @@ export type J2sFormState = {
|
|
|
33
34
|
export default class J2sForm extends LightningElementWithState<J2sFormState> {
|
|
34
35
|
private _countriesJson = "";
|
|
35
36
|
private _countriesLookup: any = {}; // TODO typings
|
|
36
|
-
private _didRenderRecaptcha = false;
|
|
37
37
|
private _didSetFormId = false;
|
|
38
|
-
private _formMetadata: any = {}; // TODO typings
|
|
39
|
-
private _incomingUserInfo: any = {}; // TODO typings
|
|
40
38
|
private _isInitialRender = true;
|
|
39
|
+
private _recaptchaToken = "";
|
|
41
40
|
|
|
42
|
-
@api
|
|
41
|
+
@api formMetadata: any = {};
|
|
43
42
|
@api id = "";
|
|
44
|
-
|
|
43
|
+
@api incomingUserInfo: any = {};
|
|
45
44
|
@api referralText = "";
|
|
46
45
|
|
|
47
46
|
@api
|
|
@@ -60,42 +59,41 @@ export default class J2sForm extends LightningElementWithState<J2sFormState> {
|
|
|
60
59
|
this._countriesLookup = countriesLookup;
|
|
61
60
|
}
|
|
62
61
|
|
|
63
|
-
@api
|
|
64
|
-
get incomingUserInfo() {
|
|
65
|
-
return this._incomingUserInfo;
|
|
66
|
-
}
|
|
67
|
-
set incomingUserInfo(value: string) {
|
|
68
|
-
this._incomingUserInfo = toJson(value);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
@api
|
|
72
|
-
get formMetadata() {
|
|
73
|
-
return this._formMetadata;
|
|
74
|
-
}
|
|
75
|
-
set formMetadata(value: string) {
|
|
76
|
-
this._formMetadata = toJson(value);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
62
|
@api
|
|
80
63
|
get formDataForDrm() {
|
|
81
64
|
// We should be able to just get the formData from the form automatically using `new FormData(theForm)`,
|
|
82
65
|
// but our custom form elements don't seem to implement what it is required for that currently. See:
|
|
83
66
|
// https://web.dev/more-capable-form-controls/#defining-a-form-associated-custom-element
|
|
84
67
|
const formData: any = {};
|
|
85
|
-
this.template
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
68
|
+
this.template
|
|
69
|
+
.querySelectorAll("form [name]")
|
|
70
|
+
.forEach((namedElement) => {
|
|
71
|
+
const typedElement = namedElement as HTMLInputElement;
|
|
72
|
+
formData[typedElement.name] =
|
|
73
|
+
typeof typedElement.checked === "boolean"
|
|
74
|
+
? typedElement.checked
|
|
75
|
+
: typedElement.value;
|
|
76
|
+
});
|
|
92
77
|
return formData;
|
|
93
78
|
}
|
|
94
79
|
|
|
80
|
+
@api
|
|
81
|
+
get recaptchaToken() {
|
|
82
|
+
return this._recaptchaToken;
|
|
83
|
+
}
|
|
84
|
+
set recaptchaToken(value: string) {
|
|
85
|
+
this._recaptchaToken = value;
|
|
86
|
+
if (!this._isInitialRender) {
|
|
87
|
+
// Treat recaptcha checkbox as a required form field even though not technically marked as such:
|
|
88
|
+
this.handleFormFieldChange({ target: { required: true } } as any);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
95
92
|
private participatingCountryCodes = ["US", "CA", "IN"];
|
|
96
93
|
private countriesWithReferralInput = ["IN"];
|
|
97
94
|
private countriesWithRequiredJobRole = ["CA"];
|
|
98
95
|
private requiredFormFields: HTMLElement[] = [];
|
|
96
|
+
private showSubmitFailureAlert = false;
|
|
99
97
|
|
|
100
98
|
get isNotParticipatingCountry() {
|
|
101
99
|
return !this.isParticipatingCountry(this.state.country);
|
|
@@ -105,12 +103,12 @@ export default class J2sForm extends LightningElementWithState<J2sFormState> {
|
|
|
105
103
|
return this.countriesWithReferralInput.includes(this.state.country);
|
|
106
104
|
}
|
|
107
105
|
|
|
108
|
-
get
|
|
109
|
-
return this.
|
|
106
|
+
get notAvailableAlertInfo() {
|
|
107
|
+
return this.formMetadata.notAvailableAlertInfo;
|
|
110
108
|
}
|
|
111
109
|
|
|
112
|
-
get
|
|
113
|
-
return
|
|
110
|
+
get formSubmitFailureAlertInfo() {
|
|
111
|
+
return this.formMetadata.formSubmitFailureAlertInfo;
|
|
114
112
|
}
|
|
115
113
|
|
|
116
114
|
get jobRoles() {
|
|
@@ -122,7 +120,7 @@ export default class J2sForm extends LightningElementWithState<J2sFormState> {
|
|
|
122
120
|
|
|
123
121
|
connectedCallback() {
|
|
124
122
|
const initialState: Partial<J2sFormState> = {
|
|
125
|
-
...this.
|
|
123
|
+
...this.incomingUserInfo,
|
|
126
124
|
isFormValid: false,
|
|
127
125
|
showMarketingText: true
|
|
128
126
|
};
|
|
@@ -132,26 +130,20 @@ export default class J2sForm extends LightningElementWithState<J2sFormState> {
|
|
|
132
130
|
if (initialCountryData) {
|
|
133
131
|
initialState.showLegalCheckbox =
|
|
134
132
|
initialCountryData.showLegalCheckbox;
|
|
133
|
+
initialState.showMarketingText =
|
|
134
|
+
initialCountryData.showMarketingText;
|
|
135
135
|
initialState.legalText = initialCountryData.legalText;
|
|
136
136
|
initialState.marketingText = initialCountryData.marketingText;
|
|
137
137
|
initialState.learnMore = initialCountryData.learnMore;
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
this.state = initialState as J2sFormState;
|
|
141
|
-
|
|
142
|
-
if ((window as any).grecaptcha) {
|
|
143
|
-
this.setRecaptcha();
|
|
144
|
-
} else {
|
|
145
|
-
window.addEventListener("recaptchaloaded", this.setRecaptcha);
|
|
146
|
-
}
|
|
147
141
|
}
|
|
148
142
|
|
|
149
143
|
renderedCallback(): void {
|
|
150
144
|
if (this._isInitialRender) {
|
|
151
|
-
this.updateInnerHtml(
|
|
152
|
-
|
|
153
|
-
(this.formMetadata as any).j2sTerms
|
|
154
|
-
);
|
|
145
|
+
this.updateInnerHtml("span.j2s-terms", this.formMetadata.j2sTerms);
|
|
146
|
+
this._isInitialRender = false;
|
|
155
147
|
}
|
|
156
148
|
|
|
157
149
|
if (
|
|
@@ -197,32 +189,49 @@ export default class J2sForm extends LightningElementWithState<J2sFormState> {
|
|
|
197
189
|
this._didSetFormId = true;
|
|
198
190
|
this.template.querySelector("form")?.setAttribute("id", this.id);
|
|
199
191
|
}
|
|
200
|
-
|
|
201
|
-
if (!this._didRenderRecaptcha && this.recaptcha) {
|
|
202
|
-
this._didRenderRecaptcha = true;
|
|
203
|
-
this.recaptcha.render(this.template.querySelector(".captcha"), {
|
|
204
|
-
sitekey: this.drmRecaptchaSiteKey,
|
|
205
|
-
callback: (recaptchaToken: string) => {
|
|
206
|
-
console.log("got captcha", recaptchaToken);
|
|
207
|
-
},
|
|
208
|
-
"expired-callback": () => {},
|
|
209
|
-
"error-callback": (...args: any[]) => {
|
|
210
|
-
console.log("RECAPTCHA ERROR");
|
|
211
|
-
console.log(...args);
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
192
|
}
|
|
216
193
|
|
|
217
|
-
|
|
218
|
-
|
|
194
|
+
handleLearnMore() {
|
|
195
|
+
this.template
|
|
196
|
+
.querySelector(".legal-terms-learn-more")
|
|
197
|
+
?.classList.toggle("hidden");
|
|
219
198
|
}
|
|
220
199
|
|
|
221
|
-
|
|
222
|
-
|
|
200
|
+
handleFormFieldChange = (evt: CustomEvent) => {
|
|
201
|
+
const callbacks: Function[] = [];
|
|
202
|
+
|
|
203
|
+
// Event target information must be read prior to debouncing; otherwise, the target is gone after the debounce.
|
|
204
|
+
if ((evt.target as HTMLInputElement)?.name === "MailingCountry") {
|
|
205
|
+
const { value } = evt.detail;
|
|
206
|
+
callbacks.push(() => this.handleCountryChange(value));
|
|
207
|
+
} else if ((evt.target as HTMLInputElement)?.required) {
|
|
208
|
+
callbacks.push(() => this.handleRequiredFieldUpdate());
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
callbacks.push(() => {
|
|
212
|
+
// Remove submit failure message once any change is made to the form.
|
|
213
|
+
if (this.showSubmitFailureAlert) {
|
|
214
|
+
this.showSubmitFailureAlert = false;
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// Don't do unnecessary work too often if form fields are rapidly being changed
|
|
219
|
+
this.debouncedFormFieldChangeHandler(callbacks);
|
|
223
220
|
};
|
|
224
221
|
|
|
225
|
-
|
|
222
|
+
@api
|
|
223
|
+
notifySubmitFailure() {
|
|
224
|
+
this.showSubmitFailureAlert = true;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private debouncedFormFieldChangeHandler = debounce(
|
|
228
|
+
(callbacks: Function[]) => {
|
|
229
|
+
callbacks.forEach((callback) => callback());
|
|
230
|
+
},
|
|
231
|
+
1000 / 15 /* 15fps seems reasonable */
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
private handleRequiredFieldUpdate() {
|
|
226
235
|
const isFormNowValid = this.checkValidity();
|
|
227
236
|
if (this.state.isFormValid !== isFormNowValid) {
|
|
228
237
|
this.setState({
|
|
@@ -231,16 +240,16 @@ export default class J2sForm extends LightningElementWithState<J2sFormState> {
|
|
|
231
240
|
}
|
|
232
241
|
}
|
|
233
242
|
|
|
234
|
-
handleCountryChange(
|
|
235
|
-
const
|
|
236
|
-
const countryData = this._countriesLookup[value];
|
|
243
|
+
private handleCountryChange(countryCode: string) {
|
|
244
|
+
const countryData = this._countriesLookup[countryCode];
|
|
237
245
|
|
|
238
246
|
this.setState({
|
|
239
|
-
country:
|
|
247
|
+
country: countryCode,
|
|
240
248
|
learnMore: countryData.learnMore,
|
|
241
249
|
legalText: countryData.legalText,
|
|
242
250
|
marketingText: countryData.marketingText,
|
|
243
|
-
showLegalCheckbox: countryData.showLegalCheckbox
|
|
251
|
+
showLegalCheckbox: countryData.showLegalCheckbox,
|
|
252
|
+
showMarketingText: countryData.showMarketingText
|
|
244
253
|
});
|
|
245
254
|
|
|
246
255
|
// Because updating the country can update the required fields, we need to re-set form
|
|
@@ -252,12 +261,6 @@ export default class J2sForm extends LightningElementWithState<J2sFormState> {
|
|
|
252
261
|
);
|
|
253
262
|
}
|
|
254
263
|
|
|
255
|
-
handleLearnMore() {
|
|
256
|
-
this.template
|
|
257
|
-
.querySelector(".legal-terms-learn-more")
|
|
258
|
-
?.classList.toggle("hidden");
|
|
259
|
-
}
|
|
260
|
-
|
|
261
264
|
private updateInnerHtml(selector: string, html: string): void {
|
|
262
265
|
const element = this.template.querySelector(selector);
|
|
263
266
|
if (element) {
|
|
@@ -271,6 +274,7 @@ export default class J2sForm extends LightningElementWithState<J2sFormState> {
|
|
|
271
274
|
|
|
272
275
|
private checkValidity() {
|
|
273
276
|
return (
|
|
277
|
+
// Boolean(this.recaptchaToken) &&
|
|
274
278
|
this.isParticipatingCountry(this.state.country) &&
|
|
275
279
|
this.requiredFormFields.every(
|
|
276
280
|
(requiredFormField) =>
|