@salesforcedevs/dw-components 1.3.175 → 1.3.176-canary.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/lwc.config.json +5 -1
- package/package.json +2 -3
- package/src/modules/dw/j2sCardSequence/j2sCardSequence.css +14 -0
- package/src/modules/dw/j2sCardSequence/j2sCardSequence.html +37 -0
- package/src/modules/dw/j2sCardSequence/j2sCardSequence.ts +418 -0
- package/src/modules/dw/j2sCardSequence/mockProps.ts +61 -0
- package/src/modules/dw/j2sCardSequenceStep/j2sCardSequenceStep.css +76 -0
- package/src/modules/dw/j2sCardSequenceStep/j2sCardSequenceStep.html +63 -0
- package/src/modules/dw/j2sCardSequenceStep/j2sCardSequenceStep.ts +152 -0
- package/src/modules/dw/j2sForm/j2sForm.css +65 -0
- package/src/modules/dw/j2sForm/j2sForm.html +208 -0
- package/src/modules/dw/j2sForm/j2sForm.ts +353 -0
- package/src/modules/dw/j2sForm/mockProps.ts +3095 -0
- package/src/modules/dwUtils/forms/forms.ts +494 -217
- package/LICENSE +0 -12
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import { toJson } from "dxUtils/normalizers";
|
|
2
|
+
import { stateMap } from "dwUtils/forms";
|
|
3
|
+
import { api } from "lwc";
|
|
4
|
+
import debounce from "debounce";
|
|
5
|
+
import { LightningElementWithState } from "dxBaseElements/lightningElementWithState";
|
|
6
|
+
import { DrmFormData } from "dw/j2sCardSequence";
|
|
7
|
+
|
|
8
|
+
// Hard-coding for now because we can't get information about an endpoint for this
|
|
9
|
+
const tbidValidRoles = [
|
|
10
|
+
"Developer",
|
|
11
|
+
"Administrator",
|
|
12
|
+
"Architect",
|
|
13
|
+
"Sales Representative / Manager",
|
|
14
|
+
"Marketer",
|
|
15
|
+
"Service Agent / Supervisor",
|
|
16
|
+
"Data Analyst",
|
|
17
|
+
"Consultant",
|
|
18
|
+
"Business Operations Manager",
|
|
19
|
+
"Designer",
|
|
20
|
+
"Educator",
|
|
21
|
+
"Student",
|
|
22
|
+
"Executive",
|
|
23
|
+
"Product Manager"
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
export type J2sFormState = DrmFormData & {
|
|
27
|
+
legalText: string;
|
|
28
|
+
marketingText: string;
|
|
29
|
+
learnMore: string;
|
|
30
|
+
showLegalCheckbox: boolean;
|
|
31
|
+
showMarketingText: boolean;
|
|
32
|
+
isFormValid: boolean;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const debounceCallback = (callback: Function) =>
|
|
36
|
+
debounce(callback, 1000 / 15 /* 15fps roughly */);
|
|
37
|
+
|
|
38
|
+
export default class J2sForm extends LightningElementWithState<J2sFormState> {
|
|
39
|
+
private _countriesJson = "";
|
|
40
|
+
private _countriesLookup: any = {}; // TODO typings
|
|
41
|
+
private _didSetFormId = false;
|
|
42
|
+
private _isInitialRender = true;
|
|
43
|
+
private _recaptchaToken = "";
|
|
44
|
+
|
|
45
|
+
@api formMetadata: any = {};
|
|
46
|
+
@api id = "";
|
|
47
|
+
@api incomingUserInfo = {} as DrmFormData;
|
|
48
|
+
@api referralText = "";
|
|
49
|
+
|
|
50
|
+
@api
|
|
51
|
+
get countries() {
|
|
52
|
+
return this._countriesJson || "[]";
|
|
53
|
+
}
|
|
54
|
+
set countries(value: string) {
|
|
55
|
+
this._countriesJson = value;
|
|
56
|
+
|
|
57
|
+
// Store a lookup table of country data by value so that we don't have to loop all the time
|
|
58
|
+
const countries = toJson(value);
|
|
59
|
+
const countriesLookup: any = {};
|
|
60
|
+
countries.forEach((country: any) => {
|
|
61
|
+
countriesLookup[country.value] = country;
|
|
62
|
+
});
|
|
63
|
+
this._countriesLookup = countriesLookup;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@api
|
|
67
|
+
get formDataForDrm(): Partial<DrmFormData> {
|
|
68
|
+
// We should be able to just get the formData from the form automatically using `new FormData(theForm)`,
|
|
69
|
+
// but our custom form elements don't seem to implement what it is required for that currently. See:
|
|
70
|
+
// https://web.dev/more-capable-form-controls/#defining-a-form-associated-custom-element
|
|
71
|
+
const formData: any = {};
|
|
72
|
+
this.template
|
|
73
|
+
.querySelectorAll("form [name]")
|
|
74
|
+
.forEach((namedElement) => {
|
|
75
|
+
const typedElement = namedElement as HTMLInputElement;
|
|
76
|
+
formData[typedElement.name] =
|
|
77
|
+
typeof typedElement.checked === "boolean"
|
|
78
|
+
? typedElement.checked
|
|
79
|
+
: typedElement.value;
|
|
80
|
+
});
|
|
81
|
+
return formData;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@api
|
|
85
|
+
get recaptchaToken() {
|
|
86
|
+
return this._recaptchaToken;
|
|
87
|
+
}
|
|
88
|
+
set recaptchaToken(value: string) {
|
|
89
|
+
this._recaptchaToken = value;
|
|
90
|
+
if (!this._isInitialRender) {
|
|
91
|
+
// Treat recaptcha checkbox as a required form field even though not technically marked as such:
|
|
92
|
+
this.setState({
|
|
93
|
+
isFormValid: this.checkValidity()
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private participatingCountryCodes = ["US", "CA", "IN"];
|
|
99
|
+
private countriesWithReferralInput = ["IN"];
|
|
100
|
+
private requiredFormFields: HTMLElement[] = [];
|
|
101
|
+
private showSubmitFailureAlert = false;
|
|
102
|
+
|
|
103
|
+
get isNotParticipatingCountry() {
|
|
104
|
+
return !this.isParticipatingCountry(this.state.MailingCountry);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
get disableCheckboxes() {
|
|
108
|
+
return this.isSalesforceEmployee || this.isNotParticipatingCountry;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
get isCountryWithReferralInput() {
|
|
112
|
+
return this.countriesWithReferralInput.includes(
|
|
113
|
+
this.state.MailingCountry
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
get isSalesforceEmployee() {
|
|
118
|
+
return this.state.RelationshipToSalesforce === "Salesforce Employee";
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
get notAvailableAlertInfo() {
|
|
122
|
+
return this.formMetadata.notAvailableAlertInfo;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
get salesforceEmployeeAlertInfo() {
|
|
126
|
+
return this.formMetadata.salesforceEmployeeAlertInfo;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
get formSubmitFailureAlertInfo() {
|
|
130
|
+
return this.formMetadata.formSubmitFailureAlertInfo;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
get jobRoles() {
|
|
134
|
+
return tbidValidRoles.map((role) => ({
|
|
135
|
+
label: role,
|
|
136
|
+
value: role
|
|
137
|
+
}));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
get showStateProvince(): boolean {
|
|
141
|
+
return this.state.MailingCountry in stateMap;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
get states(): { id: string; label: string; value: string }[] {
|
|
145
|
+
return (
|
|
146
|
+
stateMap[this.state.MailingCountry as keyof typeof stateMap] || []
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
connectedCallback() {
|
|
151
|
+
const initialState: Partial<J2sFormState> = {
|
|
152
|
+
...this.incomingUserInfo,
|
|
153
|
+
isFormValid: false,
|
|
154
|
+
showMarketingText: true
|
|
155
|
+
};
|
|
156
|
+
const initialCountry = initialState.MailingCountry;
|
|
157
|
+
const initialCountryData =
|
|
158
|
+
initialCountry && this._countriesLookup[initialCountry];
|
|
159
|
+
if (initialCountryData) {
|
|
160
|
+
initialState.showLegalCheckbox =
|
|
161
|
+
initialCountryData.showLegalCheckbox;
|
|
162
|
+
initialState.showMarketingText =
|
|
163
|
+
initialCountryData.showMarketingText;
|
|
164
|
+
initialState.legalText = initialCountryData.legalText;
|
|
165
|
+
initialState.marketingText = initialCountryData.marketingText;
|
|
166
|
+
initialState.learnMore = initialCountryData.learnMore;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
this.state = initialState as J2sFormState;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
renderedCallback(): void {
|
|
173
|
+
if (this._isInitialRender) {
|
|
174
|
+
this.updateInnerHtml("span.j2s-terms", this.formMetadata.j2sTerms);
|
|
175
|
+
this._isInitialRender = false;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (
|
|
179
|
+
this.state.MailingCountry &&
|
|
180
|
+
this.state.MailingCountry !== this.prevState.MailingCountry
|
|
181
|
+
) {
|
|
182
|
+
// Re-render new country's terms
|
|
183
|
+
this.updateInnerHtml(
|
|
184
|
+
"span.marketing-terms",
|
|
185
|
+
this.state.marketingText
|
|
186
|
+
);
|
|
187
|
+
this.updateInnerHtml("span.legal-terms", this.state.legalText);
|
|
188
|
+
if (this.state.learnMore) {
|
|
189
|
+
this.updateInnerHtml(
|
|
190
|
+
"span.legal-terms-learn-more",
|
|
191
|
+
this.state.learnMore
|
|
192
|
+
);
|
|
193
|
+
} else {
|
|
194
|
+
this.updateInnerHtml("span.legal-terms-learn-more", "");
|
|
195
|
+
this.template
|
|
196
|
+
.querySelector(".legal-terms-learn-more")
|
|
197
|
+
?.classList.add("hidden");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (this.state.isFormValid !== this.prevState.isFormValid) {
|
|
202
|
+
this.dispatchEvent(
|
|
203
|
+
new CustomEvent("formvaliditychange", {
|
|
204
|
+
detail: {
|
|
205
|
+
isValid: this.state.isFormValid
|
|
206
|
+
},
|
|
207
|
+
bubbles: true,
|
|
208
|
+
composed: true
|
|
209
|
+
})
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
this.requiredFormFields = Array.from(
|
|
214
|
+
this.template.querySelectorAll("[required]")
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
if (this.id && !this._didSetFormId) {
|
|
218
|
+
this._didSetFormId = true;
|
|
219
|
+
this.template.querySelector("form")?.setAttribute("id", this.id);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
handleLearnMore() {
|
|
224
|
+
this.template
|
|
225
|
+
.querySelector(".legal-terms-learn-more")
|
|
226
|
+
?.classList.toggle("hidden");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
debouncedFormFieldChangeHandler = debounceCallback(
|
|
230
|
+
(targetName: string, value: any, isRequiredField = false) => {
|
|
231
|
+
if (targetName === "MailingCountry") {
|
|
232
|
+
this.handleCountryChange(value);
|
|
233
|
+
} else {
|
|
234
|
+
const nextState: Partial<J2sFormState> = {};
|
|
235
|
+
if (targetName) {
|
|
236
|
+
nextState[targetName as keyof J2sFormState] = value;
|
|
237
|
+
}
|
|
238
|
+
if (isRequiredField) {
|
|
239
|
+
nextState.isFormValid = this.checkValidity();
|
|
240
|
+
}
|
|
241
|
+
this.setState(nextState);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
this.unsetSubmitFailure();
|
|
245
|
+
}
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
handleFormFieldChange = (evt: CustomEvent) => {
|
|
249
|
+
// Event target information must be read prior to debouncing; the target is gone after the debounce.
|
|
250
|
+
const { target = {}, detail } = evt;
|
|
251
|
+
const { name: targetName, required: isRequiredField } =
|
|
252
|
+
target as HTMLInputElement;
|
|
253
|
+
const value =
|
|
254
|
+
typeof detail === "string"
|
|
255
|
+
? detail
|
|
256
|
+
: typeof detail.checked === "boolean"
|
|
257
|
+
? detail.checked
|
|
258
|
+
: detail.value;
|
|
259
|
+
|
|
260
|
+
// Perform the real work after a debounce, in order to prevent unnecessary work on rapid form value changes:
|
|
261
|
+
this.debouncedFormFieldChangeHandler(
|
|
262
|
+
targetName,
|
|
263
|
+
value,
|
|
264
|
+
isRequiredField
|
|
265
|
+
);
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
@api
|
|
269
|
+
notifySubmitFailure(formSubmissionFailureState: Partial<DrmFormData>) {
|
|
270
|
+
this.showSubmitFailureAlert = true;
|
|
271
|
+
// After form submission failure, restore at most the following values to the form:
|
|
272
|
+
const {
|
|
273
|
+
FirstName,
|
|
274
|
+
LastName,
|
|
275
|
+
Email,
|
|
276
|
+
MailingCountry,
|
|
277
|
+
MailingState,
|
|
278
|
+
Company__c,
|
|
279
|
+
DRM_Role__c,
|
|
280
|
+
Referral_Email__c
|
|
281
|
+
} = formSubmissionFailureState;
|
|
282
|
+
this.setState({
|
|
283
|
+
FirstName,
|
|
284
|
+
LastName,
|
|
285
|
+
Email,
|
|
286
|
+
MailingCountry,
|
|
287
|
+
MailingState,
|
|
288
|
+
Company__c,
|
|
289
|
+
DRM_Role__c,
|
|
290
|
+
Referral_Email__c
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
private unsetSubmitFailure() {
|
|
295
|
+
if (this.showSubmitFailureAlert) {
|
|
296
|
+
this.showSubmitFailureAlert = false;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private handleCountryChange(countryCode: string) {
|
|
301
|
+
const countryData = this._countriesLookup[countryCode];
|
|
302
|
+
|
|
303
|
+
this.setState({
|
|
304
|
+
MailingCountry: countryCode,
|
|
305
|
+
MailingState: undefined,
|
|
306
|
+
learnMore: countryData.learnMore,
|
|
307
|
+
legalText: countryData.legalText,
|
|
308
|
+
marketingText: countryData.marketingText,
|
|
309
|
+
showLegalCheckbox: countryData.showLegalCheckbox,
|
|
310
|
+
showMarketingText: countryData.showMarketingText
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
this.dispatchEvent(
|
|
314
|
+
new CustomEvent("countrychange", {
|
|
315
|
+
bubbles: true,
|
|
316
|
+
composed: true,
|
|
317
|
+
detail: countryCode
|
|
318
|
+
})
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
// Because updating the country can update the required fields, we need to re-set form
|
|
322
|
+
// validity _after_ render
|
|
323
|
+
requestAnimationFrame(() =>
|
|
324
|
+
this.setState({
|
|
325
|
+
isFormValid: this.checkValidity()
|
|
326
|
+
})
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
private updateInnerHtml(selector: string, html: string): void {
|
|
331
|
+
const element = this.template.querySelector(selector);
|
|
332
|
+
if (element) {
|
|
333
|
+
element.innerHTML = html;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
private isParticipatingCountry(country: string) {
|
|
338
|
+
return this.participatingCountryCodes.includes(country);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
private checkValidity() {
|
|
342
|
+
return (
|
|
343
|
+
Boolean(this.recaptchaToken) &&
|
|
344
|
+
!this.isSalesforceEmployee &&
|
|
345
|
+
this.isParticipatingCountry(this.state.MailingCountry) &&
|
|
346
|
+
this.requiredFormFields.every(
|
|
347
|
+
(requiredFormField) =>
|
|
348
|
+
// @ts-ignore
|
|
349
|
+
requiredFormField.checkValidity?.() === true
|
|
350
|
+
)
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
}
|