@salesforcedevs/dx-components 1.3.66 → 1.3.71

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 (31) hide show
  1. package/package.json +2 -2
  2. package/src/modules/dx/breadcrumbs/breadcrumbs.ts +10 -8
  3. package/src/modules/dx/cardBlogPost/cardBlogPost.ts +1 -1
  4. package/src/modules/dx/cardCallout/cardCallout.ts +4 -4
  5. package/src/modules/dx/cardContent/cardContent.html +1 -4
  6. package/src/modules/dx/cardDocs/cardDocs.html +1 -4
  7. package/src/modules/dx/cardDocs/cardDocs.ts +7 -6
  8. package/src/modules/dx/codeBlock/codeBlock.ts +38 -18
  9. package/src/modules/dx/dropdown/dropdown.ts +3 -2
  10. package/src/modules/dx/dropdownOption/dropdownOption.ts +1 -1
  11. package/src/modules/dx/featuredContentHeader/featuredContentHeader.ts +1 -1
  12. package/src/modules/dx/filterMenu/filterMenu.html +2 -7
  13. package/src/modules/dx/filterMenu/filterMenu.ts +3 -4
  14. package/src/modules/dx/footer/links.ts +4 -2
  15. package/src/modules/dx/footerOption/footerOption.ts +2 -1
  16. package/src/modules/dx/grid/grid.ts +1 -3
  17. package/src/modules/dx/mainContentHeader/mainContentHeader.ts +4 -4
  18. package/src/modules/dx/popover/popover.ts +3 -3
  19. package/src/modules/dx/scrollManager/scrollManager.ts +85 -16
  20. package/src/modules/dx/searchResults/searchResults.ts +3 -1
  21. package/src/modules/dx/select/select.ts +3 -2
  22. package/src/modules/dx/sidebar/sidebar.ts +5 -9
  23. package/src/modules/dx/sidebarOld/sidebarOld.ts +0 -2
  24. package/src/modules/dx/sidebarSearch/sidebarSearch.ts +5 -2
  25. package/src/modules/dx/tab/tab.ts +1 -1
  26. package/src/modules/dx/tabPanelList/tabPanelList.ts +2 -2
  27. package/src/modules/dx/toc/toc.ts +2 -2
  28. package/src/modules/dx/treeItem/treeItem.ts +3 -2
  29. package/src/modules/dxBaseElements/headerBase/headerBase.ts +1 -1
  30. package/src/modules/dxBaseElements/matchMediaElement/matchMediaElement.ts +4 -2
  31. package/src/modules/dxUtils/prismjs/prismjs.ts +167 -287
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforcedevs/dx-components",
3
- "version": "1.3.66",
3
+ "version": "1.3.71",
4
4
  "description": "DX Lightning web components",
5
5
  "license": "MIT",
6
6
  "engines": {
@@ -40,5 +40,5 @@
40
40
  "eventsourcemock": "^2.0.0",
41
41
  "luxon": "^3.1.0"
42
42
  },
43
- "gitHead": "f20982f83f9a0eeaf9b4dcbcd48396af8c341736"
43
+ "gitHead": "2189b873b414cccf79905cf0b1401db76abbd9d0"
44
44
  }
@@ -85,14 +85,16 @@ export default class Breadcrumbs extends LightningElement {
85
85
  breadcrumbs: Breadcrumb[]
86
86
  ): NormalizedBreadcrumb[] {
87
87
  const { length } = breadcrumbs;
88
- return breadcrumbs.map((breadcrumb, i): NormalizedBreadcrumb => {
89
- const last = i === length - 1;
90
- return {
91
- ...breadcrumb,
92
- asLink: this.hideCurrentLocation || !last,
93
- last
94
- };
95
- });
88
+ return breadcrumbs.map(
89
+ (breadcrumb, i): NormalizedBreadcrumb => {
90
+ const last = i === length - 1;
91
+ return {
92
+ ...breadcrumb,
93
+ asLink: this.hideCurrentLocation || !last,
94
+ last
95
+ };
96
+ }
97
+ );
96
98
  }
97
99
 
98
100
  private get breadcrumbDropdownOptions() {
@@ -24,7 +24,7 @@ export default class CardBlogPost extends LightningElement {
24
24
  const payload = {
25
25
  click_text: this.title,
26
26
  click_url: this.href,
27
- element_title: "dx-card-blog-post",
27
+ element_title: this.title,
28
28
  element_type: "card",
29
29
  content_category: "link"
30
30
  };
@@ -37,12 +37,12 @@ export default class CardCallout extends LightningElement {
37
37
  elementType: "card callout",
38
38
  destinationType: "internal"
39
39
  });
40
- track(e.currentTarget!, "custEv_linkClick", {
40
+ track(e.currentTarget!, "custEv_ctaLinkClick", {
41
41
  click_text: this.title,
42
42
  click_url: this.href,
43
- element_title: "dx-card-callout",
44
- element_type: "card",
45
- content_category: "link"
43
+ element_title: this.title,
44
+ element_type: "link",
45
+ content_category: "cta"
46
46
  });
47
47
  }
48
48
  }
@@ -21,10 +21,7 @@
21
21
  />
22
22
  </a>
23
23
  <div
24
- class="
25
- dx-card-base_section-vertical dx-card-base_column
26
- card_section-text
27
- "
24
+ class="dx-card-base_section-vertical dx-card-base_column card_section-text"
28
25
  >
29
26
  <span if:true={label} part="label" class="label dx-text-label-3">
30
27
  {label}
@@ -7,10 +7,7 @@
7
7
  class="image dx-card-base_image"
8
8
  />
9
9
  <div
10
- class="
11
- dx-card-base_section-vertical dx-card-base_column
12
- card_section-text
13
- "
10
+ class="dx-card-base_section-vertical dx-card-base_column card_section-text"
14
11
  >
15
12
  <span class="dx-text-label-3">{label}</span>
16
13
  <dx-card-title
@@ -27,7 +27,6 @@ export default class CardDocs extends LightningElement {
27
27
 
28
28
  private onSlotChange(e: LightningSlotElement) {
29
29
  const payloadInnerButton = {
30
- element_title: "dx-button",
31
30
  element_type: "link",
32
31
  content_category: "card"
33
32
  };
@@ -38,11 +37,13 @@ export default class CardDocs extends LightningElement {
38
37
  slotElement?.addEventListener("click", (event: Event) => {
39
38
  track(event.currentTarget!, "custEv_cardClick", {
40
39
  ...payloadInnerButton,
40
+ element_title: slotElement.innerText,
41
41
  click_text: slotElement.innerText,
42
42
  click_url: slotElement.href
43
43
  });
44
44
  track(event.currentTarget!, "custEv_linkClick", {
45
45
  ...payloadInnerButton,
46
+ element_title: slotElement.innerText,
46
47
  click_text: slotElement.innerText,
47
48
  click_url: slotElement.href
48
49
  });
@@ -52,21 +53,21 @@ export default class CardDocs extends LightningElement {
52
53
  }
53
54
 
54
55
  private handleLinkClick(event: PointerEvent) {
55
- const paylodCardInfo = {
56
+ const payloadCardInfo = {
56
57
  click_text: this.title,
57
- element_title: "dx-card-title",
58
+ element_title: this.title,
58
59
  click_url: this.href,
59
60
  element_type: "link",
60
61
  content_category: "cta"
61
62
  };
62
- track(event.currentTarget!, "custEv_cardClick", paylodCardInfo);
63
- track(event.currentTarget!, "custEv_linkClick", paylodCardInfo);
63
+ track(event.currentTarget!, "custEv_cardClick", payloadCardInfo);
64
+ track(event.currentTarget!, "custEv_linkClick", payloadCardInfo);
64
65
  }
65
66
 
66
67
  private handleTextClick(event: PointerEvent) {
67
68
  const payloadCardTextInfo = {
68
69
  click_text: this.body,
69
- element_title: "span",
70
+ element_title: this.body,
70
71
  click_url: this.href,
71
72
  element_type: "tile",
72
73
  content_category: "cta"
@@ -68,10 +68,9 @@ export default class CodeBlock extends LightningElement {
68
68
  set codeBlock(value: string) {
69
69
  this._codeBlockRendered = false;
70
70
  let match;
71
- this._codeBlock = (
72
- (match = preTagRegexp.exec(value.trim())) === null
73
- ? value.trim()
74
- : match[1]
71
+ this._codeBlock = ((match = preTagRegexp.exec(value.trim())) === null
72
+ ? value.trim()
73
+ : match[1]
75
74
  ).trim();
76
75
  }
77
76
 
@@ -108,23 +107,35 @@ export default class CodeBlock extends LightningElement {
108
107
  formatCodeBlock() {
109
108
  const divEl = this.template.querySelector("div.code-block-content");
110
109
  const templateEl = document.createElement("template");
110
+
111
+ // Replace any <var> markup with a temporary nonsense sentinel (but one that is very
112
+ // unlikely to affect Prism's tokenization) so that Prism will not strip them but does
113
+ // still tokenize correctly. We want to italicize the "variables" ourselves after Prism
114
+ // does its own thing (W-11975205).
115
+ let cleanCodeBlock = this.codeBlock.replace(
116
+ /<var.*?>(.+?)<\/var>/g,
117
+ "vvvvv$1vvvvv"
118
+ );
119
+
111
120
  if (
112
121
  !this.isEncoded &&
113
122
  this.markupLangs.includes(this.selectedLanguage.id || "")
114
123
  ) {
115
- // eslint-disable-next-line
116
- templateEl.innerHTML = `<pre class='codeblock'><!--${this.codeBlock.replace(
124
+ // Temporarily replace HTML comment characters, which Prism would also strip
125
+ cleanCodeBlock = `<!--${cleanCodeBlock.replace(
117
126
  /<!--(.*?)-->/gs,
118
127
  "@@$1##"
119
- )}--></pre>`;
128
+ )}-->`;
120
129
  } else {
121
- // eslint-disable-next-line
122
- const innerHtml = this.isEncoded
123
- ? this.codeBlock
124
- : this.codeBlock.replace(/</g, "&lt").replace(/>/g, "&gt");
125
-
126
- templateEl.innerHTML = `<pre class='codeblock'>${innerHtml}</pre>`;
130
+ // If this is a non-encoded markup language, encode angle brackets that Prism would strip
131
+ cleanCodeBlock = this.isEncoded
132
+ ? cleanCodeBlock
133
+ : cleanCodeBlock.replace(/</g, "&lt").replace(/>/g, "&gt");
127
134
  }
135
+
136
+ // eslint-disable-next-line
137
+ templateEl.innerHTML = `<pre class='codeblock'>${cleanCodeBlock}</pre>`;
138
+
128
139
  const codeBlockEls = templateEl.content.querySelectorAll("pre");
129
140
  codeBlockEls.forEach((codeBlockEl) => {
130
141
  // eslint-disable-next-line
@@ -141,12 +152,19 @@ export default class CodeBlock extends LightningElement {
141
152
  // for custom markup content, it is a workaround to be refactored later.
142
153
  // eslint-disable-next-line
143
154
  this.language !== "text"
144
- ? (codeEl.innerHTML = codeHTML)
155
+ ? // eslint-disable-next-line
156
+ (codeEl.innerHTML = codeHTML)
145
157
  : (codeEl.textContent = this._codeBlock.trim());
146
158
  // eslint-disable-next-line
147
159
  codeBlockEl.innerHTML = "";
148
160
  codeBlockEl.appendChild(codeEl);
149
161
  Prism.highlightAllUnder(codeBlockEl);
162
+ // Italicize anything marked as a "variable" by the docs team
163
+ // eslint-disable-next-line
164
+ codeBlockEl.innerHTML = codeBlockEl.innerHTML.replace(
165
+ /vvvvv(.+?)vvvvv/g,
166
+ "<span class='token italic'>$1</span>"
167
+ );
150
168
  });
151
169
 
152
170
  if (divEl) {
@@ -156,6 +174,7 @@ export default class CodeBlock extends LightningElement {
156
174
  if (this.markupLangs.includes(this.selectedLanguage.id || "")) {
157
175
  const res = this.template.querySelector(`code.language-markup`);
158
176
  if (res) {
177
+ // Restore any temporarily replaced HTML comment characters
159
178
  // eslint-disable-next-line
160
179
  res.innerHTML = res.innerHTML.replace(
161
180
  /@@(.*?)##/gs,
@@ -193,13 +212,14 @@ export default class CodeBlock extends LightningElement {
193
212
  gtmTrack(event.target, "custEv_iconClick", {
194
213
  click_text: this.copyBtnText,
195
214
  element_type: "icon",
196
- element_title: "dx-icon",
215
+ element_title: this.copyBtnText,
197
216
  content_category: "code block"
198
217
  });
199
218
 
200
219
  try {
201
- const snippetContainer: HTMLElement | null =
202
- this.template.querySelector(".code-block-content");
220
+ const snippetContainer: HTMLElement | null = this.template.querySelector(
221
+ ".code-block-content"
222
+ );
203
223
  if (snippetContainer && snippetContainer.textContent) {
204
224
  await navigator.clipboard.writeText(
205
225
  snippetContainer.textContent
@@ -221,7 +241,7 @@ export default class CodeBlock extends LightningElement {
221
241
  gtmTrack(event.target, "custEv_iconClick", {
222
242
  click_text: this.updateThemeBtnText,
223
243
  element_type: "icon",
224
- element_title: "dx-icon",
244
+ element_title: this.updateThemeBtnText,
225
245
  content_category: "code block"
226
246
  });
227
247
  }
@@ -172,8 +172,9 @@ export default class Dropdown extends LightningElement {
172
172
  ? this.findOptionElementIndex(this.value)
173
173
  : 0;
174
174
 
175
- const optionToFocus: any =
176
- this.optionsElements[defaultIndex < 0 ? 0 : defaultIndex];
175
+ const optionToFocus: any = this.optionsElements[
176
+ defaultIndex < 0 ? 0 : defaultIndex
177
+ ];
177
178
  if (optionToFocus) {
178
179
  optionToFocus.focus();
179
180
  this._focusedValue = optionToFocus.option.id;
@@ -49,7 +49,7 @@ export default class DropdownOption extends LightningElement {
49
49
  click_url: this.option.link?.href,
50
50
  element_type: this.analyticsPayload?.element_type || "dropdown",
51
51
  ...(this.analyticsPayload?.nav_type
52
- ? { nav_item: this.option.label }
52
+ ? { nav_item: `${this.navItemLabel}:${this.option.label}` }
53
53
  : {})
54
54
  });
55
55
  }
@@ -205,7 +205,7 @@ export default class FeaturedContentHeader extends LightningElement {
205
205
  track(event.target!, trackEvent, {
206
206
  media_name: this.title,
207
207
  media_action: action,
208
- media_episode: this.href,
208
+ media_episode: this.title.split(":")[0],
209
209
  media_percentage_played: percentagePlayed,
210
210
  media_seconds_played: timePlayed,
211
211
  media_type: "podcast"
@@ -62,10 +62,7 @@
62
62
  stroke-width="2"
63
63
  stroke-linecap="round"
64
64
  stroke-linejoin="round"
65
- class="
66
- feather feather-chevron-down
67
- year-caret
68
- "
65
+ class="feather feather-chevron-down year-caret"
69
66
  >
70
67
  <polyline
71
68
  points="6 9 12 15 18 9"
@@ -88,9 +85,7 @@
88
85
  {month.id}&nbsp;
89
86
  <span
90
87
  if:true={filtersDatesLoading}
91
- class="
92
- filters-dates-loading
93
- "
88
+ class="filters-dates-loading"
94
89
  ></span>
95
90
  <span
96
91
  if:false={filtersDatesLoading}
@@ -80,10 +80,9 @@ export default class FilterMenu extends LightningElement {
80
80
 
81
81
  private setIntedeterminateStatus(checkbox: any, value: boolean) {
82
82
  if ("isYear" in checkbox.value && !checkbox.value.isYear) {
83
- const yearCheckBox: any =
84
- checkbox.parentElement.parentElement.parentElement.querySelector(
85
- ".checkbox-year"
86
- );
83
+ const yearCheckBox: any = checkbox.parentElement.parentElement.parentElement.querySelector(
84
+ ".checkbox-year"
85
+ );
87
86
  yearCheckBox.indeterminate = value;
88
87
  }
89
88
  }
@@ -81,7 +81,8 @@ export const generalLinksRaw: OptionWithRequiredNested[] = [
81
81
  options: [
82
82
  {
83
83
  link: {
84
- href: "https://trailhead.salesforce.com/trailblazer-community/feed"
84
+ href:
85
+ "https://trailhead.salesforce.com/trailblazer-community/feed"
85
86
  },
86
87
  label: "Trailblazer Community",
87
88
  id: "Trailblazer Community"
@@ -158,7 +159,8 @@ export const termsLinks = [
158
159
  label: "Legal"
159
160
  },
160
161
  {
161
- href: "https://www.salesforce.com/company/privacy/full_privacy/#nav_info",
162
+ href:
163
+ "https://www.salesforce.com/company/privacy/full_privacy/#nav_info",
162
164
  label: "Use of Cookies"
163
165
  },
164
166
  { href: "https://trust.salesforce.com/en/", label: "Trust" },
@@ -13,12 +13,13 @@ export default class FooterOption extends LightningElement {
13
13
  click_text: this.label || undefined,
14
14
  click_url: this.href || undefined,
15
15
  nav_item: this.label || undefined,
16
+ nav_level: "2",
16
17
  element_type: "link",
17
18
  nav_type: "footer"
18
19
  });
19
20
  track(e.currentTarget!, "custEv_linkClick", {
20
21
  click_text: this.label,
21
- element_title: "dx-button",
22
+ element_title: this.label,
22
23
  click_url: this.href,
23
24
  element_type: "link",
24
25
  content_category: "cta"
@@ -67,8 +67,6 @@ export default class Grid extends LightningElement {
67
67
  }
68
68
 
69
69
  onSlotChange(e: Event) {
70
- this._itemCount = (
71
- e.target as LightningSlotElement
72
- ).assignedElements().length;
70
+ this._itemCount = (e.target as LightningSlotElement).assignedElements().length;
73
71
  }
74
72
  }
@@ -41,12 +41,12 @@ export default class MainContentHeader extends LightningElement {
41
41
  click_text: this.ctaLabel,
42
42
  click_url: this.ctaHref,
43
43
  element_type: "button",
44
- element_title: "dx-button",
44
+ element_title: this.ctaLabel,
45
45
  content_category: "cta"
46
46
  });
47
47
  track(e.currentTarget!, "custEv_linkClick", {
48
48
  click_text: this.ctaLabel,
49
- element_title: "dx-button",
49
+ element_title: this.ctaLabel,
50
50
  click_url: this.ctaHref,
51
51
  element_type: "link",
52
52
  content_category: "cta"
@@ -60,12 +60,12 @@ export default class MainContentHeader extends LightningElement {
60
60
  click_text: this.ctaLabelSecondary,
61
61
  click_url: this.ctaHrefSecondary,
62
62
  element_type: "button",
63
- element_title: "dx-button",
63
+ element_title: this.ctaLabelSecondary,
64
64
  content_category: "cta"
65
65
  });
66
66
  track(e.currentTarget!, "custEv_linkClick", {
67
67
  click_text: this.ctaLabelSecondary,
68
- element_title: "dx-button",
68
+ element_title: this.ctaLabelSecondary,
69
69
  click_url: this.ctaHrefSecondary,
70
70
  element_type: "link",
71
71
  content_category: "cta"
@@ -246,9 +246,9 @@ export default class Popover extends LightningElement {
246
246
  const elements = slot.assignedElements();
247
247
  const slotted = elements.length === 0 ? null : elements[0];
248
248
  // allows dropdown/select to compose popover
249
- const slotElement = (
250
- slotted?.tagName === "SLOT" ? slotted.firstChild : slotted
251
- ) as HTMLElement | null;
249
+ const slotElement = (slotted?.tagName === "SLOT"
250
+ ? slotted.firstChild
251
+ : slotted) as HTMLElement | null;
252
252
  const isWorkToDo =
253
253
  slotElement &&
254
254
  (!this.control || !slotElement.isSameNode(this.control));
@@ -1,5 +1,6 @@
1
1
  import { LightningElement } from "lwc";
2
2
  import { throttle } from "throttle-debounce";
3
+ import { track } from "dxUtils/analytics";
3
4
 
4
5
  const RESTORE_SCROLL_EVENT_NAME = "restore-scroll";
5
6
  const LOAD_TIME_SCROLL_RESTORE_DELAY = 750;
@@ -17,29 +18,36 @@ export const restoreScroll = () => {
17
18
  return;
18
19
  }
19
20
  document.body.scrollTop = document.documentElement.scrollTop =
20
- window.history.state?.scroll.value;
21
+ window.history.state?.scroll?.value;
21
22
  };
22
23
 
23
24
  export default class ScrollManager extends LightningElement {
24
25
  /*
25
- WARNING: Dark Magic(TM) follows. This code is likely to be unreliable if:
26
- - Our load times significantly change (in this case, LOAD_TIME_SCROLL_RESTORE_DELAY may need to be adjusted)
27
- - Any other components attempt to manipulate the body scroll (including #a links) before LOAD_TIME_SCROLL_RESTORE_DELAY has expired
28
-
29
- This Dark Magic(TM) was required because of the following super annoying race condition occuring while trying to restore scroll position:
30
- 1. Load a page on our site (reproducible consistently at time of writing at https://developer.salesforce.com)
31
- 2. Scroll down somewhere (remember where)
32
- 3. Navigate to another page
33
- 4. Navigate back/forward/back, using the browser forward/back buttons (sometimes it takes a few tries to reproduce the race condition)
34
- 5. If the page you're on takes long enough for its components to finish rendering asynchronously, sometimes your scroll position will be incorrect
35
- (until the second scroll restore is called. To see the really bad behavior, simply remove the window.setTimeout bit)
36
- This is because basically it scrolls you down a bit, then a component gets bigger above you as it finishes rendering
37
- This pushes your whole view down, so unless we do this Dark Magic(TM) where we attempt to restore scroll _again_
38
- after some reasonable interval, your window will be in the wrong spot when everything finishes rendering
39
- */
26
+ WARNING: Dark Magic(TM) follows. This code is likely to be unreliable if:
27
+ - Our load times significantly change (in this case, LOAD_TIME_SCROLL_RESTORE_DELAY may need to be adjusted)
28
+ - Any other components attempt to manipulate the body scroll (including #a links) before LOAD_TIME_SCROLL_RESTORE_DELAY has expired
29
+
30
+ This Dark Magic(TM) was required because of the following super annoying race condition occuring while trying to restore scroll position:
31
+ 1. Load a page on our site (reproducible consistently at time of writing at https://developer.salesforce.com)
32
+ 2. Scroll down somewhere (remember where)
33
+ 3. Navigate to another page
34
+ 4. Navigate back/forward/back, using the browser forward/back buttons (sometimes it takes a few tries to reproduce the race condition)
35
+ 5. If the page you're on takes long enough for its components to finish rendering asynchronously, sometimes your scroll position will be incorrect
36
+ (until the second scroll restore is called. To see the really bad behavior, simply remove the window.setTimeout bit)
37
+ This is because basically it scrolls you down a bit, then a component gets bigger above you as it finishes rendering
38
+ This pushes your whole view down, so unless we do this Dark Magic(TM) where we attempt to restore scroll _again_
39
+ after some reasonable interval, your window will be in the wrong spot when everything finishes rendering
40
+ */
40
41
 
41
42
  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
42
43
 
44
+ private scrollPosition = 0;
45
+ private scrolledTwentyFivePercent = false;
46
+ private scrolledFiftyPercent = false;
47
+ private scrolledSevenFivePercent = false;
48
+ private scrolledOneHundredPercent = false;
49
+ private scrollUnlocked = false;
50
+
43
51
  renderedCallback() {
44
52
  scrollUnlocked = window.location.hash !== ""; // if we have anchor links, skip the entire scroll restore
45
53
  if (!globalThis.singletonScrollManagerRendered && !scrollUnlocked) {
@@ -51,12 +59,22 @@ export default class ScrollManager extends LightningElement {
51
59
  // only do this if loading is complete and the scrollHeight matches expectations
52
60
  // otherwise, we're likely still loading, so just chill to avoid jumping around so much
53
61
  restoreScroll();
62
+ this.setScrolledPercentagesFromRestore(
63
+ this.calculateScrollPercentage(
64
+ +window.history.state?.scroll?.value
65
+ )
66
+ );
54
67
  } else {
55
68
  window.setTimeout(() => {
56
69
  // sometimes loading is slow, so we may want to reset the scroll to
57
70
  // the correct position after loading is complete, to avoid weird behavior
58
71
  // but only if the user hasn't scrolled around in the meantime
59
72
  restoreScroll();
73
+ this.setScrolledPercentagesFromRestore(
74
+ this.calculateScrollPercentage(
75
+ +window.history.state?.scroll?.value
76
+ )
77
+ );
60
78
  scrollUnlocked = true;
61
79
  }, LOAD_TIME_SCROLL_RESTORE_DELAY);
62
80
  }
@@ -80,6 +98,56 @@ export default class ScrollManager extends LightningElement {
80
98
  }
81
99
  }
82
100
 
101
+ sendGtmScrollThresholdEvent(threshold: "25" | "50" | "75" | "100") {
102
+ track(document.body, "custEv_scroll", {
103
+ scrollDepth: threshold
104
+ });
105
+ }
106
+
107
+ calculateScrollPercentage(scrollTop: number = document.body.scrollTop) {
108
+ return (
109
+ (scrollTop / (document.body.scrollHeight - window.innerHeight)) *
110
+ 100
111
+ );
112
+ }
113
+
114
+ // do not re-send scrolled events when scroll position is restored and user scrolls up
115
+ setScrolledPercentagesFromRestore(scrollPercentage: number) {
116
+ if (scrollPercentage > 25) {
117
+ this.scrolledTwentyFivePercent = true;
118
+ }
119
+ if (scrollPercentage > 50) {
120
+ this.scrolledFiftyPercent = true;
121
+ }
122
+ if (scrollPercentage > 75) {
123
+ this.scrolledSevenFivePercent = true;
124
+ }
125
+ if (scrollPercentage > 100) {
126
+ this.scrolledOneHundredPercent = true;
127
+ }
128
+ }
129
+
130
+ scrollThresholdHandler() {
131
+ this.scrollPosition = this.calculateScrollPercentage();
132
+
133
+ if (this.scrollPosition > 25 && !this.scrolledTwentyFivePercent) {
134
+ this.scrolledTwentyFivePercent = true;
135
+ this.sendGtmScrollThresholdEvent("25");
136
+ } else if (this.scrollPosition > 50 && !this.scrolledFiftyPercent) {
137
+ this.scrolledFiftyPercent = true;
138
+ this.sendGtmScrollThresholdEvent("50");
139
+ } else if (this.scrollPosition > 75 && !this.scrolledSevenFivePercent) {
140
+ this.scrolledSevenFivePercent = true;
141
+ this.sendGtmScrollThresholdEvent("75");
142
+ } else if (
143
+ this.scrollPosition === 100 &&
144
+ !this.scrolledOneHundredPercent
145
+ ) {
146
+ this.scrolledOneHundredPercent = true;
147
+ this.sendGtmScrollThresholdEvent("100");
148
+ }
149
+ }
150
+
83
151
  saveScroll = throttle(100, () => {
84
152
  window.history.replaceState(
85
153
  {
@@ -101,6 +169,7 @@ export default class ScrollManager extends LightningElement {
101
169
  }
102
170
  if (this.scrollUnlocked) {
103
171
  this.saveScroll();
172
+ this.scrollThresholdHandler();
104
173
  }
105
174
  };
106
175
 
@@ -17,7 +17,9 @@ interface CoveoSearch {
17
17
 
18
18
  declare const Coveo: CoveoSearch;
19
19
 
20
- function getPaginationState(event: CoveoSDK.IQuerySuccessEventArgs): {
20
+ function getPaginationState(
21
+ event: CoveoSDK.IQuerySuccessEventArgs
22
+ ): {
21
23
  numberOfPages: number;
22
24
  currentPage: number;
23
25
  } {
@@ -79,8 +79,9 @@ export default class Select extends LightningElement {
79
79
 
80
80
  get selectElement() {
81
81
  if (!this._selectElement) {
82
- this._selectElement =
83
- this.template.querySelector<HTMLSelectElement>("select");
82
+ this._selectElement = this.template.querySelector<HTMLSelectElement>(
83
+ "select"
84
+ );
84
85
  }
85
86
  return this._selectElement;
86
87
  }
@@ -51,11 +51,9 @@ export default class Sidebar extends SidebarBase {
51
51
 
52
52
  @api
53
53
  setInputValue(searchTerm: string) {
54
- (
55
- this.template.querySelector(
56
- "dx-sidebar-search"
57
- ) as unknown as SidebarSearch
58
- )?.setInputValue(searchTerm);
54
+ ((this.template.querySelector(
55
+ "dx-sidebar-search"
56
+ ) as unknown) as SidebarSearch)?.setInputValue(searchTerm);
59
57
  }
60
58
 
61
59
  private expanded: boolean = true;
@@ -169,8 +167,6 @@ export default class Sidebar extends SidebarBase {
169
167
 
170
168
  private onSelect(event: CustomEvent) {
171
169
  this._value = event.detail.name;
172
-
173
- this.dispatchEvent(new CustomEvent("sidebarclick"));
174
170
  }
175
171
 
176
172
  private onToggleClick(event: Event) {
@@ -217,9 +213,9 @@ export default class Sidebar extends SidebarBase {
217
213
  return;
218
214
  }
219
215
 
220
- const search = this.template.querySelector(
216
+ const search = (this.template.querySelector(
221
217
  "dx-sidebar-search"
222
- ) as unknown as SidebarSearch;
218
+ ) as unknown) as SidebarSearch;
223
219
 
224
220
  if (
225
221
  this.isNearBottomOfSearchResults &&
@@ -95,8 +95,6 @@ export default class Sidebar extends SidebarBase {
95
95
 
96
96
  private onSelect(event: CustomEvent) {
97
97
  this._value = event.detail.name;
98
-
99
- this.dispatchEvent(new CustomEvent("sidebarclick"));
100
98
  }
101
99
 
102
100
  private onToggleClick(event: Event) {