@salesforcedevs/dx-components 1.3.38 → 1.3.40-alpha.0

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.
Files changed (36) hide show
  1. package/package.json +4 -3
  2. package/src/assets/icons/terms-privacy/privacyicon.png +0 -0
  3. package/src/modules/dx/cardBlogPost/cardBlogPost.html +1 -3
  4. package/src/modules/dx/cardBlogPost/cardBlogPost.ts +1 -3
  5. package/src/modules/dx/cardContent/cardContent.css +5 -0
  6. package/src/modules/dx/cardContent/cardContent.html +10 -6
  7. package/src/modules/dx/cardContent/cardContent.ts +22 -7
  8. package/src/modules/dx/cardExpanded/cardExpanded.css +15 -5
  9. package/src/modules/dx/cardExpanded/cardExpanded.html +9 -5
  10. package/src/modules/dx/cardExpanded/cardExpanded.ts +24 -8
  11. package/src/modules/dx/cardTrial/cardTrial.html +2 -0
  12. package/src/modules/dx/cardTrial/cardTrial.ts +32 -1
  13. package/src/modules/dx/cardTrialExpanded/cardTrialExpanded.html +1 -0
  14. package/src/modules/dx/cardTrialExpanded/cardTrialExpanded.ts +13 -0
  15. package/src/modules/dx/codeBlock/codeBlock.ts +10 -4
  16. package/src/modules/dx/dropdown/dropdown.html +2 -4
  17. package/src/modules/dx/dropdown/dropdown.ts +2 -7
  18. package/src/modules/dx/dropdownOption/dropdownOption.ts +11 -21
  19. package/src/modules/dx/featuredContentHeader/featuredContentHeader.ts +1 -1
  20. package/src/modules/dx/footer/footer.css +5 -0
  21. package/src/modules/dx/footer/footer.html +6 -0
  22. package/src/modules/dx/footer/footer.ts +4 -8
  23. package/src/modules/dx/footer/links.ts +5 -0
  24. package/src/modules/dx/footerOption/footerOption.ts +5 -4
  25. package/src/modules/dx/groupText/groupText.ts +6 -12
  26. package/src/modules/dx/headerNav/headerNav.html +1 -0
  27. package/src/modules/dx/headerNav/headerNav.ts +4 -0
  28. package/src/modules/dx/mainContentHeader/mainContentHeader.html +2 -2
  29. package/src/modules/dx/mainContentHeader/mainContentHeader.ts +11 -3
  30. package/src/modules/dx/pagination/pagination.ts +6 -9
  31. package/src/modules/dx/scrollManager/scrollManager.ts +14 -9
  32. package/src/modules/dx/tab/tab.ts +6 -3
  33. package/src/modules/dx/treeItem/treeItem.ts +14 -5
  34. package/src/modules/dxBaseElements/headerBase/headerBase.ts +6 -6
  35. package/src/modules/dxUtils/normalizers/normalizers.ts +1 -3
  36. package/LICENSE +0 -12
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforcedevs/dx-components",
3
- "version": "1.3.38",
3
+ "version": "1.3.40-alpha.0",
4
4
  "description": "DX Lightning web components",
5
5
  "license": "MIT",
6
6
  "engines": {
@@ -13,6 +13,7 @@
13
13
  "@coveo/headless": "^1.32.0",
14
14
  "@floating-ui/dom": "^1.0.4",
15
15
  "@sfdocs-internal/wires": "^0.6.3",
16
+ "@types/throttle-debounce": "^5.0.0",
16
17
  "@vimeo/player": "^2.16.4",
17
18
  "classnames": "^2.2.6",
18
19
  "coveo-search-ui": "^2.10082.5",
@@ -23,6 +24,7 @@
23
24
  "lodash.kebabcase": "^4.1.1",
24
25
  "microtip": "0.2.2",
25
26
  "salesforce-oauth2": "^0.2.0",
27
+ "throttle-debounce": "^5.0.0",
26
28
  "uuid": "^9.0.0"
27
29
  },
28
30
  "devDependencies": {
@@ -37,6 +39,5 @@
37
39
  "@types/vimeo__player": "^2.16.2",
38
40
  "eventsourcemock": "^2.0.0",
39
41
  "luxon": "^3.1.0"
40
- },
41
- "gitHead": "9cc8fbc4b276d28eb128f5f7fd1eec7844e831b2"
42
+ }
42
43
  }
@@ -1,8 +1,6 @@
1
1
  <template>
2
2
  <dx-card-content
3
- author-href={authorHref}
4
- author-img-src={authorImgSrc}
5
- author-label={authorLabel}
3
+ authors={authors}
6
4
  body={body}
7
5
  featured={featured}
8
6
  href={href}
@@ -1,9 +1,6 @@
1
1
  import { LightningElement, api } from "lwc";
2
2
 
3
3
  export default class CardBlogPost extends LightningElement {
4
- @api authorHref?: string | null = null;
5
- @api authorImgSrc!: string | null;
6
- @api authorLabel!: string | null;
7
4
  @api body!: string;
8
5
  @api datetime!: string;
9
6
  @api dateTime?: string | null = null;
@@ -14,6 +11,7 @@ export default class CardBlogPost extends LightningElement {
14
11
  @api label?: string;
15
12
  @api target?: string | null = null;
16
13
  @api title!: string;
14
+ @api authors?: Array<any> | null = null;
17
15
 
18
16
  // LWC is being compiled so that datetime is being interpreted as date-time
19
17
  // ONLY from from the implementation in dx-card-blog-post-provider
@@ -7,6 +7,11 @@
7
7
  height: 100%;
8
8
  }
9
9
 
10
+ .authors > *:not(:last-child) {
11
+ padding-bottom: 6px;
12
+ display: block;
13
+ }
14
+
10
15
  .card-content {
11
16
  display: flex;
12
17
  justify-content: flex-start;
@@ -42,12 +42,16 @@
42
42
  >
43
43
  {subtitle}
44
44
  </span>
45
- <dx-image-and-label
46
- if:true={hasAuthor}
47
- href={authorHref}
48
- img-src={authorImgSrc}
49
- label={authorLabel}
50
- ></dx-image-and-label>
45
+ <div class="authors" if:true={authors}>
46
+ <template for:each={authors} for:item="author">
47
+ <dx-image-and-label
48
+ key={author.key}
49
+ img-src={author.imgSrc}
50
+ label={author.name}
51
+ href={author.href}
52
+ ></dx-image-and-label>
53
+ </template>
54
+ </div>
51
55
  <span if:true={body} class="body dx-text-body-2" part="body">
52
56
  {body}
53
57
  </span>
@@ -3,11 +3,10 @@ import cx from "classnames";
3
3
  import { LightningSlotElement } from "typings/custom";
4
4
  import { isSlotEmpty } from "dxUtils/slot";
5
5
  import { toDxColor } from "dxUtils/css";
6
+ import { toJson } from "dxUtils/normalizers";
7
+ import { ImageAndLabel } from "../imageAndLabel";
6
8
 
7
9
  export default class CardContent extends LightningElement {
8
- @api authorHref?: string | null = null;
9
- @api authorImgSrc?: string | null = null;
10
- @api authorLabel?: string | null = null;
11
10
  @api backgroundColor?: string | null = null;
12
11
  @api body?: string | null = null;
13
12
  @api featured?: boolean = false;
@@ -19,14 +18,30 @@ export default class CardContent extends LightningElement {
19
18
  @api subtitle?: string | null = null;
20
19
  @api target?: string | null = null;
21
20
  @api title!: string;
21
+ @api
22
+ get authors() {
23
+ if (this._authors && this._authors.length) {
24
+ return this._authors!.map((author, index) => ({
25
+ ...author,
26
+ imgSrc: author.image_src,
27
+ key: index
28
+ }));
29
+ }
30
+ return undefined;
31
+ }
32
+
33
+ set authors(value: any) {
34
+ if (value !== "undefined") {
35
+ this._authors = (toJson(value) as Array<any>)?.filter(
36
+ (author) => author.name
37
+ );
38
+ }
39
+ }
40
+ private _authors?: Array<ImageAndLabel>;
22
41
 
23
42
  private isDatetimeEmpty: boolean = true;
24
43
  private isLinkHovered: boolean = false;
25
44
 
26
- private get hasAuthor() {
27
- return this.authorImgSrc && this.authorLabel;
28
- }
29
-
30
45
  private get backgroundStyle(): string {
31
46
  return this.backgroundColor
32
47
  ? `background: ${toDxColor(this.backgroundColor)}`
@@ -12,6 +12,7 @@ dx-formatted-date-time,
12
12
  .podcast-length {
13
13
  /* Push down a little to align with dx-image-and-label */
14
14
  margin-top: 3px;
15
+ margin-right: var(--dx-g-spacing-sm);
15
16
  }
16
17
 
17
18
  .card-body {
@@ -19,8 +20,7 @@ dx-formatted-date-time,
19
20
  }
20
21
 
21
22
  .card-extra-info {
22
- gap: var(--dx-g-spacing-sm);
23
- align-items: center;
23
+ text-align: start;
24
24
  }
25
25
 
26
26
  .card-container {
@@ -28,11 +28,19 @@ dx-formatted-date-time,
28
28
  padding-bottom: var(--dx-g-spacing-xl);
29
29
  flex-direction: column;
30
30
  gap: var(--dx-g-spacing-smd);
31
+ display: flex;
31
32
  }
32
33
 
33
- .card-container,
34
- .card-extra-info {
35
- display: flex;
34
+ .card-extra-info .authors {
35
+ margin-bottom: var(--dx-g-spacing-smd);
36
+ }
37
+
38
+ .card-extra-info .authors > *:not(:last-child) {
39
+ padding-right: var(--dx-g-spacing-md);
40
+ }
41
+
42
+ .card-extra-info .authors > * {
43
+ display: inline-flex;
36
44
  }
37
45
 
38
46
  .card-dot-separator {
@@ -43,6 +51,8 @@ dx-formatted-date-time,
43
51
  background-color: var(--dx-g-text-label-color);
44
52
  border-radius: 100%;
45
53
  margin-bottom: 3px;
54
+ display: inline-block;
55
+ margin-right: var(--dx-g-spacing-sm);
46
56
  }
47
57
 
48
58
  .card-mobile .card-body {
@@ -8,11 +8,15 @@
8
8
  title={title}
9
9
  ></dx-card-title>
10
10
  <div class="card-extra-info dx-text-label-1-dark">
11
- <dx-image-and-label
12
- if:true={renderAuthor}
13
- img-src={authorImage}
14
- label={authorLabel}
15
- ></dx-image-and-label>
11
+ <div class="authors" if:true={renderAuthor}>
12
+ <template for:each={authors} for:item="author">
13
+ <dx-image-and-label
14
+ key={author.key}
15
+ label={author.name}
16
+ img-src={author.imgSrc}
17
+ ></dx-image-and-label>
18
+ </template>
19
+ </div>
16
20
  <span class="podcast-length" if:true={renderLength}>{length}</span>
17
21
  <span if:true={renderDot} class="card-dot-separator"></span>
18
22
  <dx-formatted-date-time
@@ -1,21 +1,41 @@
1
1
  import { api } from "lwc";
2
2
  import { ArchiveCard } from "dxBaseElements/archiveCard";
3
3
  import cx from "classnames";
4
+ import ImageAndLabel from "../imageAndLabel";
5
+ import { toJson } from "dxUtils/normalizers";
4
6
 
5
7
  export default class ExpandedCard extends ArchiveCard {
6
8
  @api body!: string;
7
9
  @api date: string | null = null;
8
10
  @api topics: Array<string> = [];
9
- @api authorLabel?: string | null = null;
10
- @api authorImgSrc?: string | null = null;
11
11
  @api length?: string;
12
+ @api
13
+ get authors() {
14
+ if (this._authors && this._authors.length) {
15
+ return this._authors!.map((author, index) => ({
16
+ ...author,
17
+ imgSrc: !this.mobile ? author.image_src : "",
18
+ key: index
19
+ }));
20
+ }
21
+ return undefined;
22
+ }
23
+
24
+ set authors(value: any) {
25
+ if (value !== "undefined") {
26
+ this._authors = (toJson(value) as Array<any>)?.filter(
27
+ (author) => author.name
28
+ );
29
+ }
30
+ }
31
+ private _authors?: Array<ImageAndLabel>;
12
32
 
13
33
  private get isBlog(): boolean {
14
34
  return this.contentType === "blog";
15
35
  }
16
36
 
17
37
  private get hasAuthor(): boolean {
18
- return !!(this.authorImgSrc && this.authorLabel);
38
+ return this.authors?.length;
19
39
  }
20
40
 
21
41
  private get renderAuthor(): boolean {
@@ -27,17 +47,13 @@ export default class ExpandedCard extends ArchiveCard {
27
47
  }
28
48
 
29
49
  private get renderDot(): boolean {
30
- return !!((this.renderAuthor || this.renderLength) && this.date);
50
+ return !!(this.renderLength && this.date);
31
51
  }
32
52
 
33
53
  private get hasTopics(): boolean {
34
54
  return !!this.topics?.length;
35
55
  }
36
56
 
37
- private get authorImage(): string {
38
- return !this.mobile && this.authorImgSrc ? this.authorImgSrc : "";
39
- }
40
-
41
57
  private get containerClass(): string {
42
58
  return cx("card-container", this.mobile && "card-mobile");
43
59
  }
@@ -69,6 +69,7 @@
69
69
  size="large"
70
70
  href={buttonOneHref}
71
71
  variant="primary"
72
+ target={buttonOneTarget}
72
73
  font="sans"
73
74
  icon-symbol={buttonOneIcon}
74
75
  icon-size={buttonOneIconSize}
@@ -82,6 +83,7 @@
82
83
  href={buttonOneHref}
83
84
  variant="primary"
84
85
  font="sans"
86
+ target={buttonOneTarget}
85
87
  icon-symbol={buttonOneIcon}
86
88
  icon-size={buttonOneIconSize}
87
89
  >
@@ -1,6 +1,7 @@
1
1
  import { LightningElement, api } from "lwc";
2
2
  import cx from "classnames";
3
3
  import { toJson } from "dxUtils/normalizers";
4
+ import { track } from "dxUtils/analytics";
4
5
 
5
6
  export default class CardTrial extends LightningElement {
6
7
  @api badgeBackgroundColor?: string = "indigo-vibrant-90";
@@ -71,6 +72,10 @@ export default class CardTrial extends LightningElement {
71
72
  return this.hasButtons ? "menu" : "link";
72
73
  }
73
74
 
75
+ get buttonOneTarget() {
76
+ return this.isExternalURL(this.buttonOneHref) ? "_blank" : null;
77
+ }
78
+
74
79
  private _details!: string[];
75
80
  private _modalDetails!: [{ title: string; subtitle: string }];
76
81
  private _modalOpen = false;
@@ -93,10 +98,36 @@ export default class CardTrial extends LightningElement {
93
98
  this._modalOpen = !this._modalOpen;
94
99
  }
95
100
 
96
- private handleClick() {
101
+ private handleClick(e: PointerEvent) {
97
102
  // card is clickable only if it doesn't contain buttons (for accessibility reasons)
98
103
  if (!this.hasButtons) {
99
104
  window.location.assign(this.href);
105
+ if (this.href.includes("signup")) {
106
+ this.handleSignUpClick(e);
107
+ }
108
+ }
109
+ }
110
+
111
+ private handleSignUpClick(e: PointerEvent) {
112
+ track(e.currentTarget!, "custEv_signupStart", {
113
+ click_text: this.label,
114
+ element_title: this.title,
115
+ element_type: "card",
116
+ click_url: this.href,
117
+ content_category: "cta"
118
+ });
119
+ }
120
+
121
+ private isExternalURL(url: string | undefined): boolean {
122
+ if (!url) {
123
+ return false;
124
+ }
125
+
126
+ try {
127
+ const value = new URL(url);
128
+ return value.host !== window.location.host;
129
+ } catch (e) {
130
+ return false;
100
131
  }
101
132
  }
102
133
  }
@@ -31,6 +31,7 @@
31
31
  class="button-cta"
32
32
  size="large"
33
33
  href={href}
34
+ onclick={handleSignUpClick}
34
35
  variant="primary"
35
36
  font="sans"
36
37
  icon-symbol={buttonIcon}
@@ -1,5 +1,6 @@
1
1
  import { LightningElement, api } from "lwc";
2
2
  import { toJson } from "dxUtils/normalizers";
3
+ import { track } from "dxUtils/analytics";
3
4
 
4
5
  export default class CardTrial extends LightningElement {
5
6
  @api badgeBackgroundColor?: string = "indigo-vibrant-90";
@@ -64,4 +65,16 @@ export default class CardTrial extends LightningElement {
64
65
  private handleModalClose() {
65
66
  this.dispatchEvent(new CustomEvent("togglemodal"));
66
67
  }
68
+
69
+ private handleSignUpClick(e: PointerEvent) {
70
+ if (this.href.includes("signup")) {
71
+ track(e.currentTarget!, "custEv_signupStart", {
72
+ click_text: this.buttonCta,
73
+ element_title: this.title,
74
+ element_type: "card",
75
+ click_url: this.href,
76
+ content_category: "cta"
77
+ });
78
+ }
79
+ }
67
80
  }
@@ -181,8 +181,11 @@ export default class CodeBlock extends LightningElement {
181
181
  * https://github.com/storybookjs/storybook/issues/13529
182
182
  */
183
183
  async copySource(event: any) {
184
- gtmTrack(event.target, "custEv_codeBlock", {
185
- codeBlockAction: "copy"
184
+ gtmTrack(event.target, "custEv_iconClick", {
185
+ clickText: this.copyBtnText,
186
+ elementType: "icon",
187
+ elementTitle: "dx-icon",
188
+ contentCategory: "code block"
186
189
  });
187
190
 
188
191
  try {
@@ -206,8 +209,11 @@ export default class CodeBlock extends LightningElement {
206
209
  this.theme = this.theme === DARK ? LIGHT : DARK;
207
210
  setLocalStorageData(LOCAL_STORAGE_KEY, this.theme);
208
211
 
209
- gtmTrack(event.target, "custEv_codeBlock", {
210
- codeBlockAction: this.theme === DARK ? "darkmode" : "lightmode"
212
+ gtmTrack(event.target, "custEv_iconClick", {
213
+ clickText: this.updateThemeBtnText,
214
+ elementType: "icon",
215
+ elementTitle: "dx-icon",
216
+ contentCategory: "code block"
211
217
  });
212
218
  }
213
219
 
@@ -21,9 +21,8 @@
21
21
  >
22
22
  <template for:each={options} for:item="option" for:index="index">
23
23
  <dx-dropdown-option
24
- suppress-gtm-nav-headings={suppressGtmNavHeadings}
25
24
  analytics-event={analyticsEvent}
26
- analytics-base-payload={analyticsBasePayload}
25
+ analytics-payload={analyticsPayload}
27
26
  active={option.active}
28
27
  key-value={option.keyValue}
29
28
  if:false={option.options}
@@ -44,9 +43,8 @@
44
43
  <span class="menu_heading">{option.label}</span>
45
44
  <template for:each={option.options} for:item="suboption">
46
45
  <dx-dropdown-option
47
- suppress-gtm-nav-headings={suppressGTMNavHeadings}
48
46
  analytics-event={analyticsEvent}
49
- analytics-base-payload={analyticsBasePayload}
47
+ analytics-payload={analyticsPayload}
50
48
  active={suboption.active}
51
49
  key={suboption.id}
52
50
  key-value={suboption.keyValue}
@@ -38,13 +38,8 @@ export default class Dropdown extends LightningElement {
38
38
  @api variant: DropdownVariant = "base";
39
39
 
40
40
  // props forwarded to dropdown option
41
- @api analyticsEvent: string | null = null;
42
- @api analyticsBasePayload: AnalyticsPayload = {
43
- elementType: "dropdown",
44
- destinationType: "internal",
45
- ctaClick: true
46
- };
47
- @api suppressGtmNavHeadings?: boolean = false;
41
+ @api analyticsEvent?: string;
42
+ @api analyticsPayload?: AnalyticsPayload;
48
43
 
49
44
  @api
50
45
  get options() {
@@ -9,12 +9,11 @@ import { track } from "dxUtils/analytics";
9
9
 
10
10
  export default class DropdownOption extends LightningElement {
11
11
  @api option!: OptionWithNested;
12
- @api suppressGtmNavHeadings: boolean = false;
13
12
  @api keyValue!: string;
14
13
  @api active: boolean = false;
15
14
  @api navItemLabel: String | null = null;
16
15
  @api analyticsEvent: string | null = null;
17
- @api analyticsBasePayload!: AnalyticsPayload;
16
+ @api analyticsPayload?: AnalyticsPayload | undefined;
18
17
  @api indexPosition: number = 0;
19
18
  @api variant: DropdownVariant = "base";
20
19
 
@@ -43,25 +42,16 @@ export default class DropdownOption extends LightningElement {
43
42
  new CustomEvent("select", { detail: this.keyValue })
44
43
  );
45
44
 
46
- const payload: any = {
47
- ...this.analyticsBasePayload,
48
- clickText: this.keyValue || undefined,
49
- itemTitle: this.option.label || undefined,
50
- clickUrl: this.option.link?.href || undefined
51
- };
52
-
53
- const navHeading = this.navItemLabel || this.option.label || undefined;
54
- const navSubHeading =
55
- (this.navItemLabel ? this.option.label : undefined) || undefined;
56
-
57
- if (!this.suppressGtmNavHeadings) {
58
- payload.navHeading = navHeading;
59
- payload.navSubHeading = navSubHeading;
60
- payload.pageLocation = window.location.pathname;
61
- }
62
-
63
- if (this.analyticsEvent && e.currentTarget) {
64
- track(e.currentTarget, this.analyticsEvent, payload);
45
+ if (this.analyticsEvent && this.analyticsPayload && e.currentTarget) {
46
+ track(e.currentTarget, this.analyticsEvent, {
47
+ ...this.analyticsPayload,
48
+ clickText: this.option.label,
49
+ clickUrl: this.option.link?.href,
50
+ elementType: this.analyticsPayload?.elementType || "dropdown",
51
+ ...(this.analyticsPayload?.navType
52
+ ? { navItem: this.option.label }
53
+ : {})
54
+ });
65
55
  }
66
56
  }
67
57
  }
@@ -39,7 +39,7 @@ export default class FeaturedContentHeader extends LightningElement {
39
39
  if (this._authors && this._authors.length) {
40
40
  return this._authors!.map((author, index) => ({
41
41
  ...author,
42
- imgSrc: author.avatar_urls?.["24"] || author.imgSrc,
42
+ imgSrc: author.image_src,
43
43
  key: index
44
44
  }));
45
45
  }
@@ -44,6 +44,11 @@ footer.signup-variant-no-signup {
44
44
  color: var(--dx-g-blue-vibrant-20);
45
45
  }
46
46
 
47
+ .term-icon {
48
+ width: 35px;
49
+ margin: 0 var(--dx-g-spacing-xs);
50
+ }
51
+
47
52
  /* TOP */
48
53
 
49
54
  .content-container_top {
@@ -139,6 +139,12 @@
139
139
  </template>
140
140
  <template if:false={term.onclick}>
141
141
  <a href={term.href} key={term.label} rel={term.rel}>
142
+ <img
143
+ if:true={term.img}
144
+ class="term-icon"
145
+ src={term.img}
146
+ alt="Footer Term Icon"
147
+ />
142
148
  {term.label}
143
149
  </a>
144
150
  </template>
@@ -18,12 +18,6 @@ const createNewsletterSignupHref = (email: string): string => {
18
18
  return PATH;
19
19
  };
20
20
 
21
- const ANALYTICS_INFO = {
22
- itemTitle: "Newsletter Sign Up Footer",
23
- elementType: "button",
24
- destinationType: "internal",
25
- ctaClick: true
26
- };
27
21
  export default class Footer extends LightningElement {
28
22
  @api locale: string | null = null;
29
23
  @api
@@ -107,9 +101,11 @@ export default class Footer extends LightningElement {
107
101
  const href = createNewsletterSignupHref(e.detail);
108
102
 
109
103
  track(e.currentTarget, "custEv_ctaButtonClick", {
110
- ...ANALYTICS_INFO,
111
104
  clickText: this.inputSubmitLabel,
112
- clickUrl: PATH
105
+ clickUrl: PATH,
106
+ elementType: "button",
107
+ elementTitle: "dx-footer",
108
+ contentCategory: "cta"
113
109
  });
114
110
 
115
111
  window.location.assign(href);
@@ -167,6 +167,11 @@ export const termsLinks = [
167
167
  rel: "nofollow",
168
168
  label: "Cookie Preferences",
169
169
  onclick: openOneTrustInfoDisplay
170
+ },
171
+ {
172
+ href: "https://www.salesforce.com/form/other/privacy-request/",
173
+ label: "Your Privacy Choices",
174
+ img: "/assets/icons/terms-privacy/privacyicon.png"
170
175
  }
171
176
  ];
172
177
 
@@ -10,10 +10,11 @@ export default class FooterOption extends LightningElement {
10
10
 
11
11
  sendGtm(e: PointerEvent) {
12
12
  track(e.currentTarget, "custEv_bottomNavLinkClick", {
13
- navHeading: this.generalLabel || undefined,
14
- navSubHeading: this.label || undefined,
15
- clickText: this.href || undefined,
16
- pageLocation: window.location.pathname || undefined
13
+ clickText: this.label || undefined,
14
+ clickUrl: this.href || undefined,
15
+ navItem: this.label || undefined,
16
+ elementType: "link",
17
+ navType: "footer"
17
18
  });
18
19
  }
19
20
  }
@@ -20,12 +20,6 @@ const subtitleClasses = {
20
20
  [MEDIUM]: "dx-text-heading-7"
21
21
  };
22
22
 
23
- const ANALYTICS_INFO = {
24
- elementType: "button",
25
- destinationType: "internal",
26
- ctaClick: true
27
- };
28
-
29
23
  export default class GroupText extends LightningElement {
30
24
  @api title!: string;
31
25
  @api body?: string;
@@ -99,13 +93,13 @@ export default class GroupText extends LightningElement {
99
93
  }
100
94
 
101
95
  private trackClick(event: Event) {
102
- const payload = {
103
- ...ANALYTICS_INFO,
96
+ track(event.target!, "custEv_ctaButtonClick", {
104
97
  clickText: event.currentTarget.innerText,
105
98
  itemTitle: event.currentTarget.innerText,
106
- clickUrl: event.currentTarget.href
107
- };
108
-
109
- track(event.target!, "custEv_ctaButtonClick", payload);
99
+ clickUrl: event.currentTarget.href,
100
+ elementType: "button",
101
+ elementTitle: "dx-group-text",
102
+ contentCategory: "cta"
103
+ });
110
104
  }
111
105
  }
@@ -14,6 +14,7 @@
14
14
  <!-- DESKTOP "MORE CONTENT" NAV ITEM -->
15
15
  <dx-dropdown
16
16
  analytics-event="custEv_topNavLinkClick"
17
+ analytics-payload={ANALYTICS_PAYLOAD}
17
18
  if:true={navItem.options}
18
19
  nav-item-label={navItem.label}
19
20
  open-on-hover
@@ -46,6 +46,10 @@ export default class HeaderNav extends LightningElement {
46
46
  }
47
47
 
48
48
  private _navItems: OptionWithNested[] = [];
49
+ private ANALYTICS_PAYLOAD = {
50
+ navLevel: "2",
51
+ navType: "global"
52
+ };
49
53
 
50
54
  private requestOpenNavMenu(e: PointerEvent) {
51
55
  const detail = (<HTMLButtonElement>e.currentTarget).getAttribute(
@@ -1,6 +1,6 @@
1
1
  <template>
2
- <div class="container">
3
- <div class="text-container" style={style}>
2
+ <div class="container" style={style}>
3
+ <div class="text-container">
4
4
  <h1 class="heading dx-text-heading-2">{title}</h1>
5
5
  <span class="body dx-text-body-1">{body}</span>
6
6
  <span if:true={subtitle} class="subtitle dx-text-heading-4b">
@@ -1,4 +1,5 @@
1
1
  import { LightningElement, api } from "lwc";
2
+ import cx from "classnames";
2
3
  import { track } from "dxUtils/analytics";
3
4
 
4
5
  export default class MainContentHeader extends LightningElement {
@@ -10,16 +11,23 @@ export default class MainContentHeader extends LightningElement {
10
11
  @api imgSrc!: string;
11
12
  @api imgSrcMobile!: string;
12
13
  @api ctaTarget?: string | null = null;
14
+ @api backgroundGradientColor?: string;
15
+
16
+ private get style() {
17
+ return cx(
18
+ this.backgroundGradientColor &&
19
+ `background: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, #ffff 100%), var(--dx-g-${this.backgroundGradientColor});`
20
+ );
21
+ }
13
22
 
14
23
  private onCtaClick(e: Event) {
15
24
  if (e.currentTarget) {
16
25
  track(e.currentTarget, "custEv_ctaButtonClick", {
17
26
  clickText: this.ctaLabel,
18
- itemTitle: this.title,
19
27
  clickUrl: this.ctaHref,
20
28
  elementType: "button",
21
- destinationType: "internal",
22
- ctaClick: true
29
+ elementTitle: "dx-button",
30
+ contentCategory: "cta"
23
31
  });
24
32
  }
25
33
  }
@@ -2,13 +2,6 @@ import classNames from "classnames";
2
2
  import { LightningElement, api } from "lwc";
3
3
  import { track } from "dxUtils/analytics";
4
4
 
5
- const ANALYTICS_INFO = {
6
- clickUrl: window.location.href,
7
- ctaClick: false,
8
- elementType: "button",
9
- destinationType: "internal"
10
- };
11
-
12
5
  enum PaginationClickType {
13
6
  Number,
14
7
  Next,
@@ -98,10 +91,14 @@ export default class Pagination extends LightningElement {
98
91
  ? page
99
92
  : (e.target as HTMLElement).dataset.partId;
100
93
 
94
+ console.log("clickURL", e.target.href);
95
+
101
96
  track(e.currentTarget!, "custEv_pagination", {
102
- ...ANALYTICS_INFO,
103
97
  clickText,
104
- itemTitle: clickText
98
+ clickUrl: window.location.href,
99
+ elementType: "button",
100
+ navType: "pagination",
101
+ navItem: clickText
105
102
  });
106
103
 
107
104
  this.dispatchEvent(new CustomEvent("pagechange", { detail: page }));
@@ -1,4 +1,5 @@
1
1
  import { LightningElement } from "lwc";
2
+ import { throttle } from "throttle-debounce";
2
3
 
3
4
  const RESTORE_SCROLL_EVENT_NAME = "restore-scroll";
4
5
  const LOAD_TIME_SCROLL_RESTORE_DELAY = 750;
@@ -8,7 +9,13 @@ declare module globalThis {
8
9
  let singletonScrollManagerConnected: boolean;
9
10
  }
10
11
  // mostly components shouldn't be using this, but there are a few exceptions such as amfReference
12
+
13
+ let scrollUnlocked: boolean = false;
14
+
11
15
  export const restoreScroll = () => {
16
+ if (scrollUnlocked) {
17
+ return;
18
+ }
12
19
  document.body.scrollTop = document.documentElement.scrollTop =
13
20
  window.history.state?.scroll.value;
14
21
  };
@@ -32,13 +39,13 @@ export default class ScrollManager extends LightningElement {
32
39
  */
33
40
 
34
41
  protected scrollCount = 0; // this is for dark magic, basically we lock the user out of scrolling in the first quarter second, unless they really mean it. We do this because load timings mean that the scroll can get messed up in that period
35
- protected scrollUnlocked = false;
36
42
 
37
43
  renderedCallback() {
38
- if (!globalThis.singletonScrollManagerRendered) {
44
+ scrollUnlocked = window.location.hash !== ""; // if we have anchor links, skip the entire scroll restore
45
+ if (!globalThis.singletonScrollManagerRendered && !scrollUnlocked) {
39
46
  globalThis.singletonScrollManagerRendered = true;
40
47
  if (
41
- window.history.state?.scroll.docSize ===
48
+ window.history.state?.scroll?.docSize ===
42
49
  document.body.scrollHeight
43
50
  ) {
44
51
  // only do this if loading is complete and the scrollHeight matches expectations
@@ -49,10 +56,8 @@ export default class ScrollManager extends LightningElement {
49
56
  // sometimes loading is slow, so we may want to reset the scroll to
50
57
  // the correct position after loading is complete, to avoid weird behavior
51
58
  // but only if the user hasn't scrolled around in the meantime
52
- if (!this.scrollUnlocked) {
53
- restoreScroll();
54
- this.scrollUnlocked = true;
55
- }
59
+ restoreScroll();
60
+ scrollUnlocked = true;
56
61
  }, LOAD_TIME_SCROLL_RESTORE_DELAY);
57
62
  }
58
63
  } else {
@@ -75,7 +80,7 @@ export default class ScrollManager extends LightningElement {
75
80
  }
76
81
  }
77
82
 
78
- saveScroll = () => {
83
+ saveScroll = throttle(100, () => {
79
84
  window.history.replaceState(
80
85
  {
81
86
  scroll: {
@@ -86,7 +91,7 @@ export default class ScrollManager extends LightningElement {
86
91
  "",
87
92
  window.location.href
88
93
  );
89
- };
94
+ });
90
95
 
91
96
  manualScrollListener = () => {
92
97
  if (this.scrollCount < 5) {
@@ -32,9 +32,12 @@ export default class Tab extends LightningElement {
32
32
 
33
33
  private sendGtm(e: PointerEvent) {
34
34
  track(e.currentTarget!, "custEv_topNavLinkClick", {
35
- navHeading: this.label || undefined,
36
- clickText: this.href || undefined,
37
- pageLocation: window.location.pathname
35
+ clickText: this.label || undefined,
36
+ clickUrl: this.href || undefined,
37
+ navLevel: "1",
38
+ navItem: this.label || undefined,
39
+ elementType: "link",
40
+ navType: "global"
38
41
  });
39
42
  }
40
43
 
@@ -97,10 +97,18 @@ export default class TreeItem extends LightningElement {
97
97
  this.sendGtm(event);
98
98
  }
99
99
 
100
- private onLinkClick(event: Event): void {
101
- if (!(this._treeNode.link && this._treeNode.link.href)) {
100
+ private preventDefaultLinkBehavior(event: Event) {
101
+ // prevent page refresh if href isn't present or link is already active
102
+ if (
103
+ !this._treeNode.link?.href ||
104
+ this._treeNode.link?.href === window.location.pathname
105
+ ) {
102
106
  event.preventDefault();
103
107
  }
108
+ }
109
+
110
+ private onLinkClick(event: Event): void {
111
+ this.preventDefaultLinkBehavior(event);
104
112
 
105
113
  if (this.isParent) {
106
114
  const isSelectAction = true;
@@ -138,11 +146,12 @@ export default class TreeItem extends LightningElement {
138
146
  }
139
147
 
140
148
  private sendGtm(event: Event) {
141
- track(event.currentTarget!, "custEv_leftNavClick", {
142
- navType: "left nav bar",
149
+ track(event.currentTarget!, "custEv_leftNavLinkClick", {
143
150
  clickText: this._treeNode.label,
144
151
  clickUrl: this.href,
145
- navItem: this.parentName
152
+ navItem: this.parentName,
153
+ navType: "left nav bar",
154
+ elementType: "link"
146
155
  });
147
156
  }
148
157
  }
@@ -21,11 +21,11 @@ const VALID_BRANDS = [
21
21
  ];
22
22
 
23
23
  export const ANALYTICS_INFO = {
24
- clickText: "Sign Up",
25
- itemTitle: "Sign Up Header Button",
24
+ click_text: "Sign Up",
25
+ element_title: "Sign Up Header Button",
26
26
  elementType: "button",
27
- destinationType: "internal",
28
- ctaClick: true
27
+ element_type: "internal",
28
+ content_category: "cta"
29
29
  };
30
30
 
31
31
  export abstract class HeaderBase extends LightningElement {
@@ -201,9 +201,9 @@ export abstract class HeaderBase extends LightningElement {
201
201
  };
202
202
 
203
203
  private handleSignUpClick(e: PointerEvent) {
204
- track(e.currentTarget!, "custEv_signupStart", {
204
+ track(e.currentTarget!, "custEv_browseTrialsClick", {
205
205
  ...ANALYTICS_INFO,
206
- clickUrl: this.signupLink
206
+ click_url: this.signupLink
207
207
  });
208
208
  }
209
209
 
@@ -58,9 +58,7 @@ export const normalizePost = ({
58
58
  body: description,
59
59
  datetime: date,
60
60
  href: url,
61
- authorHref: get(authors, "[0].link"),
62
- authorImgSrc: get(authors, "[0].avatar_urls.48"),
63
- authorName: get(authors, "[0].name"),
61
+ authors: authors,
64
62
  imgAlt: "Blogpost image",
65
63
  imgSrc: featuredImage,
66
64
  id,
package/LICENSE DELETED
@@ -1,12 +0,0 @@
1
- Copyright (c) 2020, Salesforce.com, Inc.
2
- All rights reserved.
3
-
4
- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5
-
6
- * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7
-
8
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9
-
10
- * Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
11
-
12
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.