@salesforcedevs/dw-components 1.3.151-canary.1 → 1.3.153

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.
@@ -1,152 +0,0 @@
1
- import { api, LightningElement } from "lwc";
2
- import { DrmFormData, StepDatum } from "dw/j2sCardSequence";
3
- import { track } from "dxUtils/analytics";
4
- import J2sForm from "dw/j2sForm";
5
-
6
- export default class J2sCardSequenceStep extends LightningElement {
7
- @api countryOptions: any[] = [];
8
- @api isLoading = false;
9
- @api recaptchaToken = "";
10
- @api selectedCountry = "";
11
- @api stepDatum!: StepDatum;
12
- @api stepIndex = 0;
13
- @api stepTotalCount = 0;
14
- @api userInfo: any = {};
15
-
16
- @api
17
- public notifyFormSubmitFailure(formFailureData: Partial<DrmFormData>) {
18
- (
19
- this.template.querySelector("dw-j2s-form") as unknown as J2sForm
20
- )?.notifySubmitFailure(formFailureData);
21
- }
22
-
23
- private _isJ2sFormInvalid = false;
24
-
25
- private get stepClassName() {
26
- return this.stepIndex === 2 && this.selectedCountry === "IN"
27
- ? "final-card-with-action"
28
- : "";
29
- }
30
-
31
- private get isJ2sFormInvalid() {
32
- return this.stepIndex === 1 && this._isJ2sFormInvalid;
33
- }
34
- private set isJ2sFormInvalid(value: boolean) {
35
- this._isJ2sFormInvalid = value;
36
- }
37
-
38
- private get primaryActionLink() {
39
- const { link } = this.stepDatum.primaryAction;
40
- return typeof link === "string"
41
- ? link
42
- : link?.[this.selectedCountry] || link?.default;
43
- }
44
-
45
- private get primaryActionText() {
46
- const { text } = this.stepDatum.primaryAction;
47
- return typeof text === "string"
48
- ? text
49
- : text[this.selectedCountry] || text.default;
50
- }
51
-
52
- private get primaryActionVariant() {
53
- if (this.stepIndex === 1 && this.isJ2sFormInvalid) {
54
- return "custom"; // override for disabled button styling
55
- }
56
- return this.stepDatum.primaryAction.variant;
57
- }
58
-
59
- private get primaryActionLinkTarget() {
60
- return this.stepDatum.primaryAction.linkTarget || "_blank";
61
- }
62
-
63
- private get showPrimaryAction() {
64
- return this.stepIndex !== 2 || this.selectedCountry === "IN";
65
- }
66
-
67
- private get showSecondaryAction() {
68
- return this.showPrimaryAction; // same requirement as above for now
69
- }
70
-
71
- renderedCallback() {
72
- this.updateStaticInnerHtml();
73
- }
74
-
75
- handleFormValidityChange({ detail }: CustomEvent) {
76
- this.isJ2sFormInvalid = !detail.isValid;
77
- }
78
-
79
- handlePrimaryAction(evt: Event) {
80
- this.track(evt);
81
-
82
- if (this.stepIndex === 0) {
83
- this.dispatchEvent(new CustomEvent("getstarted"));
84
- } else if (this.stepIndex === 1 && !this.isJ2sFormInvalid) {
85
- const j2sFormEl = this.template.querySelector(
86
- "dw-j2s-form"
87
- ) as unknown as J2sForm;
88
- const formData = j2sFormEl.formDataForDrm;
89
-
90
- // For some countries, explicit approvals will be present; for others, it is implicitly assumed
91
- if (typeof formData.Opt_In__c !== "boolean") {
92
- formData.Opt_In__c = true;
93
- }
94
- if (typeof formData.Privacy_Statement__c !== "boolean") {
95
- formData.Privacy_Statement__c = true;
96
- }
97
-
98
- this.dispatchEvent(
99
- new CustomEvent("formsubmit", {
100
- detail: formData
101
- })
102
- );
103
- }
104
- }
105
-
106
- private updateStaticInnerHtml() {
107
- const { bodyHtml, secondaryActionHtml } = this.stepDatum;
108
- const bodyMarkupContainer =
109
- this.template.querySelector("span.body-markup");
110
- const secondaryActionMarkupContainer = this.template.querySelector(
111
- "span.secondary-action-markup"
112
- );
113
-
114
- if (bodyMarkupContainer) {
115
- let markup =
116
- typeof bodyHtml === "string"
117
- ? bodyHtml
118
- : bodyHtml?.[this.selectedCountry] || bodyHtml?.default;
119
-
120
- if (this.stepIndex === 2) {
121
- // User's email is supposed to show on final step
122
- markup = `${markup}${
123
- !this.userInfo.Email
124
- ? ""
125
- : `<span class="final-email-address">${this.userInfo.Email}</span>`
126
- }`;
127
- }
128
-
129
- bodyMarkupContainer.innerHTML = markup || "";
130
- }
131
-
132
- if (secondaryActionMarkupContainer) {
133
- // new special exception on step 2 to only show secondary action for IN
134
- secondaryActionMarkupContainer.innerHTML =
135
- secondaryActionHtml && this.showSecondaryAction
136
- ? secondaryActionHtml
137
- : "";
138
- }
139
- }
140
-
141
- private track(evt: Event) {
142
- const target = evt.target as HTMLButtonElement;
143
-
144
- track(target, "custEv_ctaButtonClick", {
145
- click_text: target.textContent,
146
- element_title: this.stepDatum.stepTitle,
147
- click_url: this.stepDatum.primaryAction.link || "",
148
- element_type: "button",
149
- content_category: "cta"
150
- });
151
- }
152
- }
@@ -1,56 +0,0 @@
1
- .hidden {
2
- display: none;
3
- }
4
-
5
- .form-row {
6
- display: flex;
7
- justify-content: space-between;
8
- padding-bottom: var(--dx-g-spacing-md);
9
- }
10
-
11
- .form-row > * {
12
- --dx-c-input-width: 100%;
13
-
14
- font-family: var(--dx-g-font-sans);
15
- width: calc(50% - 20px);
16
- }
17
-
18
- .form-row > :only-child {
19
- max-width: 100%;
20
- width: 100%;
21
- }
22
-
23
- .form-row .terms {
24
- --dx-c-checkbox-text-line-height: var(--dx-g-text-lg);
25
- --dx-c-checkbox-font-size: var(--dx-g-text-sm);
26
-
27
- font-family: var(--dx-g-font-sans);
28
- font-size: var(--dx-c-checkbox-font-size);
29
- font-weight: var(--dx-g-font-normal);
30
- }
31
-
32
- .form-row .legal-terms-learn-more:not(.hidden) {
33
- display: block;
34
- padding-top: var(--dx-g-spacing-md);
35
- }
36
-
37
- .form-row .legal-terms.no-checkbox {
38
- font-family: var(--dx-g-font-sans);
39
- font-size: var(--dx-g-text-xs);
40
- font-weight: var(--dx-g-font-normal);
41
- margin-left: 28px;
42
- }
43
-
44
- .form-terms-container {
45
- padding-top: var(--dx-g-spacing-sm);
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
- }
@@ -1,162 +0,0 @@
1
- <template>
2
- <!-- For the names of the form fields, see: https://salesforce.quip.com/daInAFETbPGm -->
3
- <form class="j2s-form">
4
- <div class="form-row">
5
- <dx-input
6
- class="first-name-field"
7
- label="First Name"
8
- missing-error-message="Enter your first name"
9
- name="FirstName"
10
- placeholder="Your first name"
11
- required
12
- validity-on-blur
13
- onchange={handleFormFieldChange}
14
- value={state.FirstName}
15
- ></dx-input>
16
- <dx-input
17
- class="last-name-field"
18
- label="Last Name"
19
- missing-error-message="Enter your last name"
20
- name="LastName"
21
- placeholder="Your last name"
22
- required
23
- validity-on-blur
24
- onchange={handleFormFieldChange}
25
- value={state.LastName}
26
- ></dx-input>
27
- </div>
28
- <div class="form-row">
29
- <dx-input
30
- class="company-name-field"
31
- label="Company Name"
32
- missing-error-message="Enter your company name"
33
- name="Company__c"
34
- placeholder="Your company name"
35
- required
36
- validity-on-blur
37
- value={state.Company__c}
38
- onchange={handleFormFieldChange}
39
- ></dx-input>
40
- <dx-select
41
- class="job-role-field"
42
- label="Job Role"
43
- message-when-value-missing="Select your job role"
44
- name="DRM_Role__c"
45
- options={jobRoles}
46
- placeholder="Select Job Role"
47
- required
48
- value={state.DRM_Role__c}
49
- onchange={handleFormFieldChange}
50
- ></dx-select>
51
- </div>
52
- <div class="form-row">
53
- <dx-input
54
- name="Email"
55
- type="email"
56
- placeholder="Your email address"
57
- label="Email"
58
- missing-error-message="Enter your email address"
59
- validity-on-blur="true"
60
- disabled
61
- readonly="true"
62
- required
63
- value={state.Email}
64
- ></dx-input>
65
- <dx-select
66
- label="Country/Region"
67
- message-when-value-missing="Select your country/region"
68
- name="MailingCountry"
69
- options={countries}
70
- placeholder="Select Country/Region"
71
- required
72
- value={state.MailingCountry}
73
- onchange={handleFormFieldChange}
74
- ></dx-select>
75
- </div>
76
- <div class="form-row" if:true={isCountryWithReferralInput}>
77
- <dx-input
78
- name="Referral_Email__c"
79
- class="referral-email-field full-width"
80
- label="If anyone has referred you, please share their Email ID below, with their consent."
81
- validity-on-blur="true"
82
- type="email"
83
- value={state.Referral_Email__c}
84
- onchange={handleFormFieldChange}
85
- ></dx-input>
86
- </div>
87
- <div class="form-row" if:true={isNotParticipatingCountry}>
88
- <dx-alert
89
- alert-info={notAvailableAlertInfo}
90
- class="full-width"
91
- type="danger"
92
- variant="accented"
93
- ></dx-alert>
94
- </div>
95
- <div class="form-row" if:true={showSubmitFailureAlert}>
96
- <dx-alert
97
- alert-info={formSubmitFailureAlertInfo}
98
- class="full-width"
99
- type="danger"
100
- variant="accented"
101
- ></dx-alert>
102
- </div>
103
- <div class="form-terms-container">
104
- <div class="form-row" if:true={state.showMarketingText}>
105
- <dx-checkbox
106
- value="marketing-terms"
107
- name="Opt_In__c"
108
- required
109
- class="terms"
110
- onchange={handleFormFieldChange}
111
- disabled={isNotParticipatingCountry}
112
- >
113
- <span lwc:dom="manual" class="marketing-terms"></span>
114
- </dx-checkbox>
115
- </div>
116
- <div class="form-row">
117
- <dx-checkbox
118
- value="j2s-terms"
119
- name="j2s_terms"
120
- required
121
- class="terms"
122
- onchange={handleFormFieldChange}
123
- disabled={isNotParticipatingCountry}
124
- >
125
- <span lwc:dom="manual" class="j2s-terms"></span>
126
- </dx-checkbox>
127
- </div>
128
- <div class="form-row">
129
- <dx-checkbox
130
- if:true={state.showLegalCheckbox}
131
- value="legal-terms"
132
- name="Privacy_Statement__c"
133
- required
134
- class="terms"
135
- onchange={handleFormFieldChange}
136
- disabled={isNotParticipatingCountry}
137
- >
138
- <span lwc:dom="manual" class="legal-terms"></span>
139
- <template if:true={state.learnMore}>
140
- &nbsp;
141
- <dx-button
142
- variant="inline-inherit"
143
- onclick={handleLearnMore}
144
- >
145
- Learn more.
146
- </dx-button>
147
- <span
148
- lwc:dom="manual"
149
- class="legal-terms-learn-more hidden"
150
- ></span>
151
- </template>
152
- </dx-checkbox>
153
- <span
154
- if:false={state.showLegalCheckbox}
155
- lwc:dom="manual"
156
- class="legal-terms no-checkbox"
157
- ></span>
158
- </div>
159
- <slot class="form-captcha-slot" name="form-captcha-slot"></slot>
160
- </div>
161
- </form>
162
- </template>
@@ -1,319 +0,0 @@
1
- import { toJson } from "dxUtils/normalizers";
2
- import { api } from "lwc";
3
- import debounce from "debounce";
4
- import { LightningElementWithState } from "dxBaseElements/lightningElementWithState";
5
- import { DrmFormData } from "dw/j2sCardSequence";
6
-
7
- // Hard-coding for now because we can't get information about an endpoint for this
8
- const tbidValidRoles = [
9
- "Developer",
10
- "Administrator",
11
- "Architect",
12
- "Sales Representative / Manager",
13
- "Marketer",
14
- "Service Agent / Supervisor",
15
- "Data Analyst",
16
- "Consultant",
17
- "Business Operations Manager",
18
- "Designer",
19
- "Educator",
20
- "Student",
21
- "Executive",
22
- "Product Manager"
23
- ];
24
-
25
- export type J2sFormState = DrmFormData & {
26
- legalText: string;
27
- marketingText: string;
28
- learnMore: string;
29
- showLegalCheckbox: boolean;
30
- showMarketingText: boolean;
31
- isFormValid: boolean;
32
- };
33
-
34
- export default class J2sForm extends LightningElementWithState<J2sFormState> {
35
- private _countriesJson = "";
36
- private _countriesLookup: any = {}; // TODO typings
37
- private _didSetFormId = false;
38
- private _isInitialRender = true;
39
- private _recaptchaToken = "";
40
-
41
- @api formMetadata: any = {};
42
- @api id = "";
43
- @api incomingUserInfo = {} as DrmFormData;
44
- @api referralText = "";
45
-
46
- @api
47
- get countries() {
48
- return this._countriesJson || "[]";
49
- }
50
- set countries(value: string) {
51
- this._countriesJson = value;
52
-
53
- // Store a lookup table of country data by value so that we don't have to loop all the time
54
- const countries = toJson(value);
55
- const countriesLookup: any = {};
56
- countries.forEach((country: any) => {
57
- countriesLookup[country.value] = country;
58
- });
59
- this._countriesLookup = countriesLookup;
60
- }
61
-
62
- @api
63
- get formDataForDrm(): Partial<DrmFormData> {
64
- // We should be able to just get the formData from the form automatically using `new FormData(theForm)`,
65
- // but our custom form elements don't seem to implement what it is required for that currently. See:
66
- // https://web.dev/more-capable-form-controls/#defining-a-form-associated-custom-element
67
- const formData: any = {};
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
- });
77
- return formData;
78
- }
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.setState({
89
- isFormValid: this.checkValidity()
90
- });
91
- }
92
- }
93
-
94
- private participatingCountryCodes = ["US", "CA", "IN"];
95
- private countriesWithReferralInput = ["IN"];
96
- private requiredFormFields: HTMLElement[] = [];
97
- private showSubmitFailureAlert = false;
98
-
99
- get isNotParticipatingCountry() {
100
- return !this.isParticipatingCountry(this.state.MailingCountry);
101
- }
102
-
103
- get isCountryWithReferralInput() {
104
- return this.countriesWithReferralInput.includes(
105
- this.state.MailingCountry
106
- );
107
- }
108
-
109
- get notAvailableAlertInfo() {
110
- return this.formMetadata.notAvailableAlertInfo;
111
- }
112
-
113
- get formSubmitFailureAlertInfo() {
114
- return this.formMetadata.formSubmitFailureAlertInfo;
115
- }
116
-
117
- get jobRoles() {
118
- return tbidValidRoles.map((role) => ({
119
- label: role,
120
- value: role
121
- }));
122
- }
123
-
124
- connectedCallback() {
125
- const initialState: Partial<J2sFormState> = {
126
- ...this.incomingUserInfo,
127
- isFormValid: false,
128
- showMarketingText: true
129
- };
130
- const initialCountry = initialState.MailingCountry;
131
- const initialCountryData =
132
- initialCountry && this._countriesLookup[initialCountry];
133
- if (initialCountryData) {
134
- initialState.showLegalCheckbox =
135
- initialCountryData.showLegalCheckbox;
136
- initialState.showMarketingText =
137
- initialCountryData.showMarketingText;
138
- initialState.legalText = initialCountryData.legalText;
139
- initialState.marketingText = initialCountryData.marketingText;
140
- initialState.learnMore = initialCountryData.learnMore;
141
- }
142
-
143
- this.state = initialState as J2sFormState;
144
- }
145
-
146
- renderedCallback(): void {
147
- if (this._isInitialRender) {
148
- this.updateInnerHtml("span.j2s-terms", this.formMetadata.j2sTerms);
149
- this._isInitialRender = false;
150
- }
151
-
152
- if (
153
- this.state.MailingCountry &&
154
- this.state.MailingCountry !== this.prevState.MailingCountry
155
- ) {
156
- // Re-render new country's terms
157
- this.updateInnerHtml(
158
- "span.marketing-terms",
159
- this.state.marketingText
160
- );
161
- this.updateInnerHtml("span.legal-terms", this.state.legalText);
162
- if (this.state.learnMore) {
163
- this.updateInnerHtml(
164
- "span.legal-terms-learn-more",
165
- this.state.learnMore
166
- );
167
- } else {
168
- this.updateInnerHtml("span.legal-terms-learn-more", "");
169
- this.template
170
- .querySelector(".legal-terms-learn-more")
171
- ?.classList.add("hidden");
172
- }
173
- }
174
-
175
- if (this.state.isFormValid !== this.prevState.isFormValid) {
176
- this.dispatchEvent(
177
- new CustomEvent("formvaliditychange", {
178
- detail: {
179
- isValid: this.state.isFormValid
180
- },
181
- bubbles: true,
182
- composed: true
183
- })
184
- );
185
- }
186
-
187
- this.requiredFormFields = Array.from(
188
- this.template.querySelectorAll("[required]")
189
- );
190
-
191
- if (this.id && !this._didSetFormId) {
192
- this._didSetFormId = true;
193
- this.template.querySelector("form")?.setAttribute("id", this.id);
194
- }
195
- }
196
-
197
- handleLearnMore() {
198
- this.template
199
- .querySelector(".legal-terms-learn-more")
200
- ?.classList.toggle("hidden");
201
- }
202
-
203
- handleFormFieldChange = (evt: CustomEvent) => {
204
- // Event target information must be read prior to debouncing; the target is gone after the debounce.
205
- const { target = {}, detail } = evt;
206
- const { name: targetName, required: isRequiredField } =
207
- target as HTMLInputElement;
208
- const value =
209
- typeof detail === "string"
210
- ? detail
211
- : typeof detail.checked === "boolean"
212
- ? detail.checked
213
- : detail.value;
214
-
215
- // Perform the real work after a debounce, in order to prevent unnecessary work on rapid form value changes:
216
- this.callbackWithDebounce(() => {
217
- if (targetName === "MailingCountry") {
218
- this.handleCountryChange(value);
219
- } else {
220
- const nextState: Partial<J2sFormState> = {};
221
- if (targetName) {
222
- nextState[targetName as keyof J2sFormState] = value;
223
- }
224
- if (isRequiredField) {
225
- nextState.isFormValid = this.checkValidity();
226
- }
227
- this.setState(nextState);
228
- }
229
-
230
- this.unsetSubmitFailure();
231
- });
232
- };
233
-
234
- @api
235
- notifySubmitFailure(formSubmissionFailureState: Partial<DrmFormData>) {
236
- this.showSubmitFailureAlert = true;
237
- // After form submission failure, restore at most the following values to the form:
238
- const {
239
- FirstName,
240
- LastName,
241
- Email,
242
- MailingCountry,
243
- Company__c,
244
- DRM_Role__c,
245
- Referral_Email__c
246
- } = formSubmissionFailureState;
247
- this.setState({
248
- FirstName,
249
- LastName,
250
- Email,
251
- MailingCountry,
252
- Company__c,
253
- DRM_Role__c,
254
- Referral_Email__c
255
- });
256
- }
257
-
258
- private unsetSubmitFailure() {
259
- if (this.showSubmitFailureAlert) {
260
- this.showSubmitFailureAlert = false;
261
- }
262
- }
263
-
264
- private callbackWithDebounce = debounce((callback: Function) => {
265
- callback();
266
- }, 1000 / 15 /* 15fps seems reasonable */);
267
-
268
- private handleCountryChange(countryCode: string) {
269
- const countryData = this._countriesLookup[countryCode];
270
-
271
- this.setState({
272
- MailingCountry: countryCode,
273
- learnMore: countryData.learnMore,
274
- legalText: countryData.legalText,
275
- marketingText: countryData.marketingText,
276
- showLegalCheckbox: countryData.showLegalCheckbox,
277
- showMarketingText: countryData.showMarketingText
278
- });
279
-
280
- this.dispatchEvent(
281
- new CustomEvent("countrychange", {
282
- bubbles: true,
283
- composed: true,
284
- detail: countryCode
285
- })
286
- );
287
-
288
- // Because updating the country can update the required fields, we need to re-set form
289
- // validity _after_ render
290
- requestAnimationFrame(() =>
291
- this.setState({
292
- isFormValid: this.checkValidity()
293
- })
294
- );
295
- }
296
-
297
- private updateInnerHtml(selector: string, html: string): void {
298
- const element = this.template.querySelector(selector);
299
- if (element) {
300
- element.innerHTML = html;
301
- }
302
- }
303
-
304
- private isParticipatingCountry(country: string) {
305
- return this.participatingCountryCodes.includes(country);
306
- }
307
-
308
- private checkValidity() {
309
- return (
310
- Boolean(this.recaptchaToken) &&
311
- this.isParticipatingCountry(this.state.MailingCountry) &&
312
- this.requiredFormFields.every(
313
- (requiredFormField) =>
314
- // @ts-ignore
315
- requiredFormField.checkValidity?.() === true
316
- )
317
- );
318
- }
319
- }