@salesforcedevs/dw-components 1.3.148-canary.14 → 1.3.148-canary.16

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": "@salesforcedevs/dw-components",
3
- "version": "1.3.148-canary.14",
3
+ "version": "1.3.148-canary.16",
4
4
  "description": "Lightning web components for https://developer.salesforce.com",
5
5
  "license": "MIT",
6
6
  "engines": {
@@ -35,7 +35,7 @@
35
35
  <dw-j2s-form
36
36
  id={stepDatum.formData.formId}
37
37
  countries={countries}
38
- form-metadata={stepDatum.formMetadata}
38
+ form-metadata={stepDatum.formData}
39
39
  incoming-user-info={formIncomingUserInfo}
40
40
  onformvaliditychange={handleFormValidityChange}
41
41
  recaptcha-token={recaptchaToken}
@@ -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;
@@ -57,30 +58,22 @@ export default class J2sCardSequence extends LightningElement {
57
58
  }
58
59
  set stepsData(value) {
59
60
  // normalize incoming value with uuid keys and non-required props (for cleaner HTML template), etc.
60
- this._stepsData = getListWithUuids<StepDatum>(toJson(value)).map(
61
- (stepDatum) => {
62
- const normalizedStepDatum = {
63
- ...stepDatum,
64
- headerData: stepDatum.headerData || {},
65
- primaryAction: {
66
- ...stepDatum.primaryAction,
67
- variant: stepDatum.primaryAction.variant || "primary",
68
- iconSprite:
69
- stepDatum.primaryAction.iconSprite || "general",
70
- iconSymbol:
71
- stepDatum.primaryAction.iconSymbol || "arrow-right"
72
- }
73
- };
74
-
75
- if (normalizedStepDatum.formData) {
76
- (normalizedStepDatum as any).formMetadata = JSON.stringify(
77
- normalizedStepDatum.formData
78
- );
61
+ const valueWithUuids = getListWithUuids<StepDatum>(toJson(value));
62
+ this._stepsData = valueWithUuids.map((stepDatum) => {
63
+ const normalizedStepDatum = {
64
+ ...stepDatum,
65
+ headerData: stepDatum.headerData || {},
66
+ primaryAction: {
67
+ ...stepDatum.primaryAction,
68
+ variant: stepDatum.primaryAction.variant || "primary",
69
+ iconSprite: stepDatum.primaryAction.iconSprite || "general",
70
+ iconSymbol:
71
+ stepDatum.primaryAction.iconSymbol || "arrow-right"
79
72
  }
73
+ };
80
74
 
81
- return normalizedStepDatum;
82
- }
83
- );
75
+ return normalizedStepDatum;
76
+ });
84
77
  }
85
78
 
86
79
  private countries = "[]";
@@ -229,9 +222,12 @@ export default class J2sCardSequence extends LightningElement {
229
222
  evt.preventDefault(); // don't proceed automatically to next step
230
223
  this._isFormSubmitting = true;
231
224
 
225
+ const j2sFormEl = (this.template.querySelector(
226
+ "dw-j2s-form"
227
+ ) as unknown) as J2sForm | null;
228
+
232
229
  try {
233
- const formData = (this.template.querySelector("dw-j2s-form") as any)
234
- ?.formDataForDrm;
230
+ const formData = j2sFormEl?.formDataForDrm;
235
231
  // For some countries, explicit approvals will be present; for others, it is implicitly assumed
236
232
  if (typeof formData.Opt_In__c !== "boolean") {
237
233
  formData.Opt_In__c = true;
@@ -265,6 +261,7 @@ export default class J2sCardSequence extends LightningElement {
265
261
  }
266
262
  }
267
263
  } catch (ex) {
264
+ j2sFormEl?.notifySubmitFailure();
268
265
  console.error(ex); // TODO
269
266
  }
270
267
 
@@ -10,7 +10,7 @@
10
10
  placeholder="Your first name"
11
11
  required
12
12
  validity-on-blur
13
- onblur={handleRequiredFieldUpdate}
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
- onblur={handleRequiredFieldUpdate}
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={handleRequiredFieldUpdate}
49
+ onchange={handleFormFieldChange}
61
50
  ></dx-select>
62
51
  </div>
63
52
  <div class="form-row">
@@ -69,8 +58,8 @@
69
58
  missing-error-message="Enter your email address"
70
59
  validity-on-blur="true"
71
60
  required
72
- onblur={handleRequiredFieldUpdate}
73
61
  value={state.email}
62
+ onchange={handleFormFieldChange}
74
63
  ></dx-input>
75
64
  <dx-select
76
65
  label="Country/Region"
@@ -80,7 +69,7 @@
80
69
  placeholder="Select Country/Region"
81
70
  required
82
71
  value={state.country}
83
- onchange={handleCountryChange}
72
+ onchange={handleFormFieldChange}
84
73
  ></dx-select>
85
74
  </div>
86
75
  <div class="form-row" if:true={isCountryWithReferralInput}>
@@ -89,6 +78,7 @@
89
78
  class="referral-email-field full-width"
90
79
  label="If anyone has referred you, please share their Email ID below, with their consent."
91
80
  type="email"
81
+ onchange={handleFormFieldChange}
92
82
  ></dx-input>
93
83
  </div>
94
84
  <div class="form-row" if:true={isNotParticipatingCountry}>
@@ -99,6 +89,14 @@
99
89
  variant="accented"
100
90
  ></dx-alert>
101
91
  </div>
92
+ <div class="form-row" if:true={showSubmitFailureAlert}>
93
+ <dx-alert
94
+ alert-info={formSubmitFailureAlertInfo}
95
+ class="full-width"
96
+ type="danger"
97
+ variant="accented"
98
+ ></dx-alert>
99
+ </div>
102
100
  <div class="form-terms-container">
103
101
  <div class="form-row" if:true={state.showMarketingText}>
104
102
  <dx-checkbox
@@ -106,7 +104,7 @@
106
104
  name="Opt_In__c"
107
105
  required
108
106
  class="terms"
109
- onchange={handleRequiredFieldUpdate}
107
+ onchange={handleFormFieldChange}
110
108
  disabled={isNotParticipatingCountry}
111
109
  >
112
110
  <span lwc:dom="manual" class="marketing-terms"></span>
@@ -118,7 +116,7 @@
118
116
  name="j2s_terms"
119
117
  required
120
118
  class="terms"
121
- onchange={handleRequiredFieldUpdate}
119
+ onchange={handleFormFieldChange}
122
120
  disabled={isNotParticipatingCountry}
123
121
  >
124
122
  <span lwc:dom="manual" class="j2s-terms"></span>
@@ -131,7 +129,7 @@
131
129
  name="Privacy_Statement__c"
132
130
  required
133
131
  class="terms"
134
- onchange={handleRequiredFieldUpdate}
132
+ onchange={handleFormFieldChange}
135
133
  disabled={isNotParticipatingCountry}
136
134
  >
137
135
  <span lwc:dom="manual" class="legal-terms"></span>
@@ -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
@@ -34,12 +35,12 @@ export default class J2sForm extends LightningElementWithState<J2sFormState> {
34
35
  private _countriesJson = "";
35
36
  private _countriesLookup: any = {}; // TODO typings
36
37
  private _didSetFormId = false;
37
- private _formMetadata: any = {}; // TODO typings
38
38
  private _incomingUserInfo: any = {}; // TODO typings
39
39
  private _isInitialRender = true;
40
40
  private _recaptchaToken = "";
41
41
 
42
42
  @api id = "";
43
+ @api formMetadata: any = {};
43
44
  @api referralText = "";
44
45
 
45
46
  @api
@@ -66,14 +67,6 @@ export default class J2sForm extends LightningElementWithState<J2sFormState> {
66
67
  this._incomingUserInfo = toJson(value);
67
68
  }
68
69
 
69
- @api
70
- get formMetadata() {
71
- return this._formMetadata;
72
- }
73
- set formMetadata(value: string) {
74
- this._formMetadata = toJson(value);
75
- }
76
-
77
70
  @api
78
71
  get formDataForDrm() {
79
72
  // We should be able to just get the formData from the form automatically using `new FormData(theForm)`,
@@ -98,13 +91,15 @@ export default class J2sForm extends LightningElementWithState<J2sFormState> {
98
91
  }
99
92
  set recaptchaToken(value: string) {
100
93
  this._recaptchaToken = value;
101
- this.handleRequiredFieldUpdate();
94
+ // Treat recaptcha checkbox as a required form field even though not technically marked as such:
95
+ this.handleFormFieldChange({ target: { required: true } } as any);
102
96
  }
103
97
 
104
98
  private participatingCountryCodes = ["US", "CA", "IN"];
105
99
  private countriesWithReferralInput = ["IN"];
106
100
  private countriesWithRequiredJobRole = ["CA"];
107
101
  private requiredFormFields: HTMLElement[] = [];
102
+ private showSubmitFailureAlert = false;
108
103
 
109
104
  get isNotParticipatingCountry() {
110
105
  return !this.isParticipatingCountry(this.state.country);
@@ -114,12 +109,12 @@ export default class J2sForm extends LightningElementWithState<J2sFormState> {
114
109
  return this.countriesWithReferralInput.includes(this.state.country);
115
110
  }
116
111
 
117
- get isJobRoleRequired() {
118
- return this.countriesWithRequiredJobRole.includes(this.state.country);
112
+ get notAvailableAlertInfo() {
113
+ return this.formMetadata.notAvailableAlertInfo;
119
114
  }
120
115
 
121
- get notAvailableAlertInfo() {
122
- return (this.formMetadata as any).notAvailableAlertInfo;
116
+ get formSubmitFailureAlertInfo() {
117
+ return this.formMetadata.formSubmitFailureAlertInfo;
123
118
  }
124
119
 
125
120
  get jobRoles() {
@@ -151,10 +146,7 @@ export default class J2sForm extends LightningElementWithState<J2sFormState> {
151
146
 
152
147
  renderedCallback(): void {
153
148
  if (this._isInitialRender) {
154
- this.updateInnerHtml(
155
- "span.j2s-terms",
156
- (this.formMetadata as any).j2sTerms
157
- );
149
+ this.updateInnerHtml("span.j2s-terms", this.formMetadata.j2sTerms);
158
150
  }
159
151
 
160
152
  if (
@@ -202,7 +194,47 @@ export default class J2sForm extends LightningElementWithState<J2sFormState> {
202
194
  }
203
195
  }
204
196
 
205
- handleRequiredFieldUpdate() {
197
+ handleLearnMore() {
198
+ this.template
199
+ .querySelector(".legal-terms-learn-more")
200
+ ?.classList.toggle("hidden");
201
+ }
202
+
203
+ handleFormFieldChange = (evt: CustomEvent) => {
204
+ const callbacks: Function[] = [];
205
+
206
+ // Event target information must be read prior to debouncing; otherwise, the target is gone after the debounce.
207
+ if ((evt.target as HTMLInputElement)?.name === "MailingCountry") {
208
+ const { value } = evt.detail;
209
+ callbacks.push(() => this.handleCountryChange(value));
210
+ } else if ((evt.target as HTMLInputElement)?.required) {
211
+ callbacks.push(() => this.handleRequiredFieldUpdate());
212
+ }
213
+
214
+ callbacks.push(() => {
215
+ // Remove submit failure message once any change is made to the form.
216
+ if (this.showSubmitFailureAlert) {
217
+ this.showSubmitFailureAlert = false;
218
+ }
219
+ });
220
+
221
+ // Don't do unnecessary work too often if form fields are rapidly being changed
222
+ this.debouncedFormFieldChangeHandler(callbacks);
223
+ };
224
+
225
+ @api
226
+ notifySubmitFailure() {
227
+ this.showSubmitFailureAlert = true;
228
+ }
229
+
230
+ private debouncedFormFieldChangeHandler = debounce(
231
+ (callbacks: Function[]) => {
232
+ callbacks.forEach((callback) => callback());
233
+ },
234
+ 1000 / 15 /* 15fps seems reasonable */
235
+ );
236
+
237
+ private handleRequiredFieldUpdate() {
206
238
  const isFormNowValid = this.checkValidity();
207
239
  if (this.state.isFormValid !== isFormNowValid) {
208
240
  this.setState({
@@ -211,12 +243,11 @@ export default class J2sForm extends LightningElementWithState<J2sFormState> {
211
243
  }
212
244
  }
213
245
 
214
- handleCountryChange({ detail }: CustomEvent) {
215
- const { value } = detail;
216
- const countryData = this._countriesLookup[value];
246
+ private handleCountryChange(countryCode: string) {
247
+ const countryData = this._countriesLookup[countryCode];
217
248
 
218
249
  this.setState({
219
- country: value,
250
+ country: countryCode,
220
251
  learnMore: countryData.learnMore,
221
252
  legalText: countryData.legalText,
222
253
  marketingText: countryData.marketingText,
@@ -232,12 +263,6 @@ export default class J2sForm extends LightningElementWithState<J2sFormState> {
232
263
  );
233
264
  }
234
265
 
235
- handleLearnMore() {
236
- this.template
237
- .querySelector(".legal-terms-learn-more")
238
- ?.classList.toggle("hidden");
239
- }
240
-
241
266
  private updateInnerHtml(selector: string, html: string): void {
242
267
  const element = this.template.querySelector(selector);
243
268
  if (element) {