@salesforcedevs/dx-components 1.3.210-lnb2-alpha → 1.3.210-lnb21-alpha

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/dx-components",
3
- "version": "1.3.210-lnb2-alpha",
3
+ "version": "1.3.210-lnb21-alpha",
4
4
  "description": "DX Lightning web components",
5
5
  "license": "MIT",
6
6
  "engines": {
@@ -272,6 +272,20 @@
272
272
  </g>
273
273
  </g>
274
274
  </symbol>
275
+ <symbol id="data-cloud" viewBox="0 0 56 56" xmlns="http://www.w3.org/2000/svg" style="fill:#fff">
276
+ <defs>
277
+ <style>.acls-2,.acls-4{stroke-width:0}.acls-2{fill:#8a8ed1}.acls-4{fill:#fff}</style>
278
+ </defs>
279
+ <path class="acls-2" d="M28 4C14.77 4 4 14.77 4 28c0 7.9 3.84 14.93 9.76 19.3.7-4.58 3.55-8.46 7.5-10.56-2.61-2.02-4.29-5.17-4.29-8.71C16.97 21.95 21.92 17 28 17s11.03 4.95 11.03 11.03c0 3.54-1.69 6.69-4.29 8.71 3.95 2.1 6.8 5.98 7.5 10.56 5.92-4.37 9.77-11.4 9.77-19.31 0-13.23-10.77-24-24-24Z"/>
280
+ <path class="acls-2" d="M11.07 18.3c.69-1.2 2.21-1.61 3.41-.92a2.5 2.5 0 0 1 .93 3.41c-.46.81-1.3 1.26-2.17 1.26-.42 0-.85-.11-1.24-.34a2.489 2.489 0 0 1-.92-3.41ZM8.49 28.06c0-1.38 1.11-2.5 2.49-2.51 1.38 0 2.51 1.11 2.51 2.5s-1.11 2.5-2.49 2.5h-.01c-1.38 0-2.5-1.11-2.5-2.49ZM14.55 38.71a2.504 2.504 0 0 1-3.42-.9c-.7-1.2-.29-2.73.9-3.42 1.19-.7 2.73-.29 3.42.9a2.51 2.51 0 0 1-.9 3.42ZM20.7 15.46c-.4.23-.83.34-1.26.34-.86 0-1.7-.44-2.16-1.24-.7-1.19-.3-2.73.9-3.42 1.19-.7 2.72-.29 3.42.9a2.51 2.51 0 0 1-.9 3.42ZM28 13.49h-.07c-1.38 0-2.5-1.11-2.5-2.49 0-1.38 1.11-2.5 2.49-2.51h.11c1.37.02 2.47 1.13 2.47 2.5s-1.12 2.5-2.5 2.5ZM38.67 14.52a2.51 2.51 0 0 1-2.17 1.24c-.43 0-.86-.1-1.25-.33a2.5 2.5 0 0 1 2.5-4.33c1.2.69 1.6 2.22.92 3.42ZM41.48 17.32a2.5 2.5 0 0 1 2.5 4.33c-.39.23-.82.33-1.25.33-.86 0-1.7-.44-2.17-1.24-.69-1.2-.28-2.73.92-3.42ZM44.91 37.73c-.46.81-1.3 1.26-2.17 1.26-.42 0-.85-.11-1.24-.34a2.493 2.493 0 0 1-.92-3.41c.69-1.2 2.22-1.61 3.42-.92a2.5 2.5 0 0 1 .91 3.41ZM45.01 30.48c-1.38 0-2.5-1.12-2.5-2.49s1.12-2.51 2.5-2.51a2.5 2.5 0 0 1 0 5Z"/>
281
+ <circle class="acls-4" cx="27.99" cy="28.03" r="7.03"/>
282
+ <path class="acls-4" d="M27.99 39.06c-5.75 0-10.42 4.67-10.42 10.42v.13C20.72 51.14 24.26 52 28 52s7.26-.86 10.41-2.38v-.14c0-5.75-4.67-10.42-10.42-10.42Z"/>
283
+ <circle cx="28" cy="28" r="28" style="stroke-width:0;fill:#200647"/>
284
+ <path class="acls-4" d="M38.41 49.62C35.26 51.14 31.73 52 28 52s-7.27-.86-10.43-2.39v-.13c0-5.75 4.67-10.42 10.42-10.42s10.42 4.67 10.42 10.42v.14Z"/>
285
+ <circle class="acls-4" cx="27.99" cy="28.03" r="7.03"/>
286
+ <path d="M42.23 47.31c-.7-4.58-3.55-8.47-7.5-10.56 2.61-2.02 4.29-5.17 4.29-8.71 0-6.08-4.95-11.03-11.03-11.03s-11.03 4.95-11.03 11.03c0 3.54 1.69 6.69 4.29 8.71-3.95 2.1-6.8 5.98-7.5 10.56-5.91-4.38-9.76-11.4-9.76-19.3C4 14.77 14.77 4 28 4s24 10.77 24 24c0 7.91-3.85 14.94-9.77 19.31Z" style="fill:#7f8ced;stroke-width:0"/>
287
+ <path class="acls-4" d="M37.75 11.1a2.5 2.5 0 0 0-2.5 4.33c.39.23.82.33 1.25.33.86 0 1.7-.44 2.17-1.24.68-1.2.28-2.73-.92-3.42ZM11 30.55c1.38 0 2.49-1.12 2.49-2.5s-1.13-2.5-2.51-2.5a2.5 2.5 0 0 0-2.49 2.51c0 1.38 1.12 2.49 2.5 2.49H11ZM42.73 21.98c.43 0 .86-.1 1.25-.33a2.5 2.5 0 0 0-2.5-4.33c-1.2.69-1.61 2.22-.92 3.42.47.8 1.31 1.24 2.17 1.24ZM11.99 21.71c.39.23.82.34 1.24.34a2.5 2.5 0 0 0 1.24-4.67c-1.2-.69-2.72-.28-3.41.92s-.28 2.73.92 3.41ZM44 34.32c-1.2-.69-2.73-.28-3.42.92-.69 1.19-.28 2.72.92 3.41.39.23.82.34 1.24.34.87 0 1.71-.45 2.17-1.26a2.5 2.5 0 0 0-.91-3.41ZM45.01 25.48c-1.38 0-2.5 1.12-2.5 2.51s1.12 2.49 2.5 2.49a2.5 2.5 0 0 0 0-5ZM28.03 8.49h-.11A2.5 2.5 0 0 0 25.43 11c0 1.38 1.12 2.49 2.5 2.49H28a2.5 2.5 0 0 0 .03-5ZM18.18 11.14a2.49 2.49 0 0 0-.9 3.42c.46.8 1.3 1.24 2.16 1.24.43 0 .86-.11 1.26-.34a2.51 2.51 0 0 0 .9-3.42c-.7-1.19-2.23-1.6-3.42-.9ZM12.03 34.39c-1.19.69-1.6 2.22-.9 3.42a2.504 2.504 0 0 0 3.42.9 2.51 2.51 0 0 0 .9-3.42 2.499 2.499 0 0 0-3.42-.9Z"/>
288
+ </symbol>
275
289
  <symbol viewBox="0 0 56 56" version="1.1" xmlns="http://www.w3.org/2000/svg" id="integration">
276
290
  <g id="Atoms" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
277
291
  <g id="ICONS-symbols" transform="translate(-1005.000000, 0.000000)" fill-rule="nonzero">
@@ -913,4 +927,31 @@
913
927
  </clipPath>
914
928
  </defs>
915
929
  </symbol>
930
+ <symbol fill="none" id="data-cloud" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 56 56">
931
+ <path fill="#8a8ed1" d="m28,4C14.77,4,4,14.77,4,28c0,7.9,3.84,14.93,9.76,19.3.7-4.58,3.55-8.46,7.5-10.56-2.61-2.02-4.29-5.17-4.29-8.71,0-6.08,4.95-11.03,11.03-11.03s11.03,4.95,11.03,11.03c0,3.54-1.69,6.69-4.29,8.71,3.95,2.1,6.8,5.98,7.5,10.56,5.92-4.37,9.77-11.4,9.77-19.31,0-13.23-10.77-24-24-24Z"/>
932
+ <path fill="#8a8ed1" d="m11.07,18.3c.69-1.2,2.21-1.61,3.41-.92,1.2.68,1.61,2.21.93,3.41h0c-.46.81-1.3,1.26-2.17,1.26-.42,0-.85-.11-1.24-.34-1.2-.68-1.61-2.21-.92-3.41Z"/>
933
+ <path fill="#8a8ed1" d="m8.49,28.06c0-1.38,1.11-2.5,2.49-2.51,1.38,0,2.51,1.11,2.51,2.5s-1.11,2.5-2.49,2.5h-.01c-1.38,0-2.5-1.11-2.5-2.49Z"/>
934
+ <path fill="#8a8ed1" d="m14.55,38.71c-.4.23-.83.34-1.26.34-.86,0-1.7-.45-2.16-1.24-.7-1.2-.29-2.73.9-3.42,1.19-.7,2.73-.29,3.42.9.69,1.19.29,2.72-.9,3.42Z"/>
935
+ <path fill="#8a8ed1" d="m20.7,15.46c-.4.23-.83.34-1.26.34-.86,0-1.7-.44-2.16-1.24-.7-1.19-.3-2.73.9-3.42,1.19-.7,2.72-.29,3.42.9.69,1.19.29,2.72-.9,3.42Z"/>
936
+ <path fill="#8a8ed1" d="m28,13.49h-.07c-1.38,0-2.5-1.11-2.5-2.49,0-1.38,1.11-2.5,2.49-2.51h.11c1.37.02,2.47,1.13,2.47,2.5s-1.12,2.5-2.5,2.5Z"/>
937
+ <path fill="#8a8ed1" d="m38.67,14.52c-.47.8-1.31,1.24-2.17,1.24-.43,0-.86-.1-1.25-.33-1.2-.69-1.6-2.22-.91-3.42.69-1.19,2.22-1.6,3.41-.91,1.2.69,1.6,2.22.92,3.42Z"/>
938
+ <path fill="#8a8ed1" d="m41.48,17.32c1.19-.69,2.72-.28,3.41.91.69,1.2.29,2.73-.91,3.42-.39.23-.82.33-1.25.33-.86,0-1.7-.44-2.17-1.24-.69-1.2-.28-2.73.92-3.42Z"/>
939
+ <path fill="#8a8ed1" d="m44.91,37.73c-.46.81-1.3,1.26-2.17,1.26-.42,0-.85-.11-1.24-.34-1.2-.69-1.61-2.22-.92-3.41.69-1.2,2.22-1.61,3.42-.92,1.19.69,1.6,2.22.91,3.41Z"/>
940
+ <path fill="#8a8ed1" d="m45.01,30.48c-1.38,0-2.5-1.12-2.5-2.49s1.12-2.51,2.5-2.51,2.5,1.12,2.5,2.5-1.12,2.5-2.5,2.5Z"/>
941
+ <circle fill="#fff" cx="27.99" cy="28.03" r="7.03"/>
942
+ <path fill="#fff" d="m27.99,39.06c-5.75,0-10.42,4.67-10.42,10.42v.13c3.15,1.53,6.69,2.39,10.43,2.39s7.26-.86,10.41-2.38v-.14c0-5.75-4.67-10.42-10.42-10.42Z"/>
943
+ <circle fill="#200647" class="cls-1" cx="28" cy="28" r="28"/>
944
+ <path fill="#fff" d="m38.41,49.62c-3.15,1.52-6.68,2.38-10.41,2.38s-7.27-.86-10.43-2.39v-.13c0-5.75,4.67-10.42,10.42-10.42s10.42,4.67,10.42,10.42v.14Z"/>
945
+ <circle fill="#fff" cx="27.99" cy="28.03" r="7.03"/>
946
+ <path fill="#7f8ced" d="m42.23,47.31c-.7-4.58-3.55-8.47-7.5-10.56,2.61-2.02,4.29-5.17,4.29-8.71,0-6.08-4.95-11.03-11.03-11.03s-11.03,4.95-11.03,11.03c0,3.54,1.69,6.69,4.29,8.71-3.95,2.1-6.8,5.98-7.5,10.56-5.91-4.38-9.76-11.4-9.76-19.3C4,14.77,14.77,4,28,4s24,10.77,24,24c0,7.91-3.85,14.94-9.77,19.31Z"/>
947
+ <path fill="#fff" d="m37.75,11.1c-1.19-.69-2.72-.28-3.41.91-.69,1.2-.29,2.73.91,3.42.39.23.82.33,1.25.33.86,0,1.7-.44,2.17-1.24.68-1.2.28-2.73-.92-3.42Z"/>
948
+ <path fill="#fff" d="m11,30.55c1.38,0,2.49-1.12,2.49-2.5s-1.13-2.5-2.51-2.5c-1.38.01-2.5,1.13-2.49,2.51,0,1.38,1.12,2.49,2.5,2.49h.01Z"/>
949
+ <path fill="#fff" d="m42.73,21.98c.43,0,.86-.1,1.25-.33,1.2-.69,1.6-2.22.91-3.42-.69-1.19-2.22-1.6-3.41-.91-1.2.69-1.61,2.22-.92,3.42.47.8,1.31,1.24,2.17,1.24Z"/>
950
+ <path fill="#fff" d="m11.99,21.71c.39.23.82.34,1.24.34.87,0,1.71-.45,2.17-1.26h0c.68-1.2.27-2.73-.93-3.41-1.2-.69-2.72-.28-3.41.92s-.28,2.73.92,3.41Z"/>
951
+ <path fill="#fff" d="m44,34.32c-1.2-.69-2.73-.28-3.42.92-.69,1.19-.28,2.72.92,3.41.39.23.82.34,1.24.34.87,0,1.71-.45,2.17-1.26.69-1.19.28-2.72-.91-3.41Z"/>
952
+ <path fill="#fff" d="m45.01,25.48c-1.38,0-2.5,1.12-2.5,2.51s1.12,2.49,2.5,2.49,2.5-1.12,2.5-2.5-1.12-2.5-2.5-2.5Z"/>
953
+ <path fill="#fff" d="m28.03,8.49h-.11c-1.38.01-2.5,1.13-2.49,2.51,0,1.38,1.12,2.49,2.5,2.49h.07c1.38,0,2.5-1.12,2.5-2.5s-1.1-2.48-2.47-2.5Z"/>
954
+ <path fill="#fff" d="m18.18,11.14c-1.2.69-1.6,2.23-.9,3.42.46.8,1.3,1.24,2.16,1.24.43,0,.86-.11,1.26-.34,1.19-.7,1.59-2.23.9-3.42-.7-1.19-2.23-1.6-3.42-.9Z"/>
955
+ <path fill="#fff" d="m12.03,34.39c-1.19.69-1.6,2.22-.9,3.42.46.79,1.3,1.24,2.16,1.24.43,0,.86-.11,1.26-.34,1.19-.7,1.59-2.23.9-3.42-.69-1.19-2.23-1.6-3.42-.9Z"/>
956
+ </symbol>
916
957
  </svg>
@@ -94,7 +94,9 @@ export const generalLinksRaw: OptionWithRequiredNested[] = [
94
94
  id: "Events and Calendar"
95
95
  },
96
96
  {
97
- link: { href: "https://go.appexchange.com/partnerprogram" },
97
+ link: {
98
+ href: "https://www.salesforce.com/partners/become-a-partner/"
99
+ },
98
100
  label: "Partner Community",
99
101
  id: "Partner Community"
100
102
  },
@@ -14,19 +14,25 @@ export default class MainContentHeader extends LightningElement {
14
14
  @api imgSrcMobile!: string;
15
15
  @api ctaTarget?: string | null = null;
16
16
  @api ctaTargetSecondary?: string | null = null;
17
- @api backgroundColor?: string;
18
- @api backgroundColorDark: boolean = false;
17
+ @api backgroundGradientColor?: string;
18
+ @api backgroundGradientDark: boolean = false;
19
19
  @api hasSwoop: boolean = false;
20
20
 
21
21
  private get style() {
22
22
  return cx(
23
- this.backgroundColor &&
24
- `background-color: var(--dx-g-${this.backgroundColor});`
23
+ this.backgroundGradientColor &&
24
+ !this.backgroundGradientDark &&
25
+ `background: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, #ffff 100%), var(--dx-g-${this.backgroundGradientColor});`,
26
+ this.backgroundGradientDark &&
27
+ `background: linear-gradient(90deg, var(--dx-g-indigo-vibrant-20) 0%, var(--dx-g-indigo-vibrant-30) 100%);`
25
28
  );
26
29
  }
27
30
 
28
31
  private get textStyle() {
29
- return cx("text-container", this.backgroundColorDark && "light-text");
32
+ return cx(
33
+ "text-container",
34
+ this.backgroundGradientDark && "light-text"
35
+ );
30
36
  }
31
37
 
32
38
  private onCtaClick(e: Event) {
@@ -164,12 +164,13 @@ export default class ScrollManager extends LightningElement {
164
164
  }
165
165
 
166
166
  saveScroll = throttle(100, () => {
167
+ const scrollingElement = document.scrollingElement || document.body;
167
168
  window.history.replaceState(
168
169
  {
169
170
  ...window.history.state,
170
171
  scroll: {
171
- value: document.body.scrollTop,
172
- docSize: document.body.scrollHeight
172
+ value: scrollingElement.scrollTop,
173
+ docSize: scrollingElement.scrollHeight
173
174
  }
174
175
  },
175
176
  "",
@@ -274,18 +274,15 @@ li.coveo-dynamic-facet-breadcrumb-value-list-item {
274
274
  }
275
275
 
276
276
  .dx-result-title {
277
+ /* override the default Coveo style */
278
+ color: var(--dx-g-blue-vibrant-20) !important;
279
+ display: block;
277
280
  font-family: var(--dx-g-font-display);
278
281
  font-size: var(--dx-g-text-lg);
279
282
  margin: 0;
280
283
  margin-bottom: var(--dx-g-spacing-sm);
281
284
  }
282
285
 
283
- .CoveoResultLink,
284
- a.CoveoResultLink,
285
- .CoveoResult a.CoveoResultLink {
286
- color: var(--dx-g-blue-vibrant-20);
287
- }
288
-
289
286
  .dx-result-excerpt {
290
287
  color: var(--dx-g-gray-10);
291
288
  font-size: 14px;
@@ -422,11 +419,17 @@ a.CoveoResultLink,
422
419
  width: fit-content;
423
420
  }
424
421
 
425
- .dx-badge {
426
- display: block;
422
+ .dx-result-info {
423
+ display: flex;
424
+ gap: 12px;
427
425
  margin-bottom: var(--dx-g-spacing-smd);
428
426
  }
429
427
 
428
+ .breadcrumb {
429
+ color: #555;
430
+ font-size: var(--dx-g-text-xs);
431
+ }
432
+
430
433
  .no-results {
431
434
  display: flex;
432
435
  justify-content: center;
@@ -6,6 +6,10 @@ import {
6
6
  CONTENT_TYPE_ICONS
7
7
  } from "dxConstants/contentTypes";
8
8
  import { getContentTypeColorVariables } from "dxUtils/contentTypes";
9
+ import { pollUntil } from "dxUtils/async";
10
+
11
+ // Max height for breadcrumb without wrapping
12
+ const MAX_BREADCRUMB_HEIGHT = 16;
9
13
 
10
14
  interface CoveoSearch {
11
15
  state: typeof CoveoSDK.state;
@@ -40,35 +44,78 @@ const resultsTemplatesInnerHtml = `
40
44
  <script
41
45
  id="myDocumentResultTemplate"
42
46
  class="result-template"
43
- type="text/html"
47
+ type="text/underscore"
44
48
  data-field-publicurl=""
45
49
  >
46
50
  <div class="dx-result">
47
- <span class="CoveoFieldValue" data-field="@content_type" data-helper="badge" data-html-value="true"></span>
48
- <p class="dx-result-title">
49
- <a
50
- class="CoveoResultLink"
51
- data-field="@publicurl"
52
- ></a>
53
- </p>
51
+ <div class="dx-result-info">
52
+ <span class="CoveoFieldValue" data-field="@content_type" data-helper="badge" data-html-value="true"></span>
53
+ <% if (!raw.breadcrumbs && !raw.metabreadcrumbs) { %>
54
+ <span class="CoveoFieldValue" data-field="@uri" data-helper="uriBreadcrumbs" data-html-value="true"></span>
55
+ <% } else { %>
56
+ <% if (raw.uri.includes('/references/')) { %>
57
+ <span class="CoveoFieldValue" data-field="@metabreadcrumbs" data-helper="metabreadcrumbs" data-html-value="true"></span>
58
+ <% } else { %>
59
+ <span class="CoveoFieldValue" data-field="@breadcrumbs" data-helper="breadcrumbs" data-html-value="true"></span>
60
+ <% } %>
61
+ <% } %>
62
+ </div>
63
+ <a
64
+ href="<%= raw.uri %>"
65
+ class="dx-result-title"
66
+ >
67
+ <%= title %>
68
+ <% if (!raw.uri.includes('developer.salesforce.com') && !raw.uri.includes('developer-website-s.herokuapp.com')) { %>
69
+ <svg xmlns="http://www.w3.org/2000/svg" style="display: inline; vertical-align: baseline;" fill="var(--dx-g-blue-vibrant-20)" width="20" height="20" part="svg" aria-hidden="true"><use xlink:href="/assets/icons/utility-sprite/svg/symbols.svg#new_window"></use></svg>
70
+ <% } %>
71
+ </a>
54
72
  <p class="dx-result-excerpt CoveoExcerpt"></p>
55
73
  </div>
56
74
  </script>
57
75
  <script
58
76
  id="myDefaultResultTemplate"
59
77
  class="result-template"
60
- type="text/html"
78
+ type="text/underscore"
61
79
  >
62
80
  <div class="dx-result">
63
- <span class="CoveoFieldValue" data-field="@content_type" data-helper="badge" data-html-value="true"></span>
64
- <p class="dx-result-title">
65
- <a class="CoveoResultLink"></a>
66
- </p>
81
+ <div class="dx-result-info">
82
+ <span class="CoveoFieldValue" data-field="@content_type" data-helper="badge" data-html-value="true"></span>
83
+ <% if (!raw.breadcrumbs && !raw.metabreadcrumbs) { %>
84
+ <span class="CoveoFieldValue" data-field="@uri" data-helper="uriBreadcrumbs" data-html-value="true"></span>
85
+ <% } else { %>
86
+ <% if (raw.uri.includes('/references/')) { %>
87
+ <span class="CoveoFieldValue" data-field="@metabreadcrumbs" data-helper="metabreadcrumbs" data-html-value="true"></span>
88
+ <% } else { %>
89
+ <span class="CoveoFieldValue" data-field="@breadcrumbs" data-helper="breadcrumbs" data-html-value="true"></span>
90
+ <% } %>
91
+ <% } %>
92
+ </div>
93
+ <a
94
+ href="<%= raw.uri %>"
95
+ class="dx-result-title"
96
+ >
97
+ <% if (title) { %>
98
+ <%= title %>
99
+ <% } else { %>
100
+ <%= uri %>
101
+ <% } %>
102
+ <% if (!raw.uri.includes('developer.salesforce.com') && !raw.uri.includes('developer-website-s.herokuapp.com')) { %>
103
+ <svg xmlns="http://www.w3.org/2000/svg" style="display: inline; vertical-align: baseline;" fill="var(--dx-g-blue-vibrant-20)" width="20" height="20" part="svg" aria-hidden="true"><use xlink:href="/assets/icons/utility-sprite/svg/symbols.svg#new_window"></use></svg>
104
+ <% } %>
105
+ </a>
67
106
  <p class="dx-result-excerpt CoveoExcerpt"></p>
68
107
  </div>
69
108
  </script>
70
109
  `;
71
110
 
111
+ const isInternalDomain = (domain: string) =>
112
+ domain === "developer.salesforce.com" ||
113
+ domain === "developer-website-s.herokuapp.com";
114
+
115
+ const isTrailheadDomain = (domain: string) =>
116
+ domain === "trailhead.salesforce.com" ||
117
+ domain === "dev.trailhead.salesforce.com";
118
+
72
119
  const buildTemplateHelperBadge = (value: keyof typeof CONTENT_TYPE_LABELS) => {
73
120
  const style = getContentTypeColorVariables(value);
74
121
  const label = CONTENT_TYPE_LABELS[value];
@@ -88,6 +135,107 @@ const buildTemplateHelperBadge = (value: keyof typeof CONTENT_TYPE_LABELS) => {
88
135
  `;
89
136
  };
90
137
 
138
+ const processParts = (parts: string[], internalFlag = false) => {
139
+ // filter /docs/ breadcrumb item from internal domains
140
+ const filterFn = internalFlag
141
+ ? (part: string) => part !== "docs"
142
+ : (part: string) => part;
143
+
144
+ return parts.filter(filterFn).map((part) => {
145
+ // Remove special characters & .htm/.xml extension
146
+ part = part
147
+ .replace(/_/g, "")
148
+ .replace(/-/g, "")
149
+ .replace(/.html*/g, "")
150
+ .replace(/.xml/g, "")
151
+ .replace(/b2c/g, "B2C");
152
+
153
+ // Capitalize first letter of each word
154
+ part = part.replace(/\w\S*/g, (w) => {
155
+ return w.replace(/^\w/, (c) => c.toUpperCase());
156
+ });
157
+
158
+ return `<span class="breadcrumb-item">${decodeURI(part)}</span>`;
159
+ });
160
+ };
161
+
162
+ const buildTemplateHelperUriBreadcrumbs = (value: string) => {
163
+ const url = new URL(value);
164
+
165
+ // exclude youtube links from breadcrumbs
166
+ const hostnamePattern = /^((www\.)?(youtube\.com|youtu\.be))$/;
167
+
168
+ // we don't want to show atlas docs because the url structure is mad ugly
169
+ if (hostnamePattern.test(url.hostname) || url.pathname.includes("atlas.")) {
170
+ return "";
171
+ }
172
+
173
+ let parts = url.pathname.split("/").filter((part) => part !== "");
174
+
175
+ // Remove language prefix from trailhead URLs
176
+ if (isTrailheadDomain(url.hostname)) {
177
+ parts = parts
178
+ .slice(1)
179
+ .filter((part) => part !== "content" && part !== "learn");
180
+ }
181
+
182
+ const breadcrumbs = processParts(parts, isInternalDomain(url.hostname));
183
+
184
+ if (!isInternalDomain(url.hostname)) {
185
+ // Remove the first breadcrumb item if it's an internal domain (i.e drop developer.salesforce.com from developer.salesforce.com / B2C Commerce / Open Commerce API / Filtering)
186
+ breadcrumbs.unshift(
187
+ `<span class="breadcrumb-item">${url.hostname}</span>`
188
+ );
189
+
190
+ return `
191
+ <span class="breadcrumb">
192
+ ${breadcrumbs.join(" / ")}
193
+ </span>
194
+ `;
195
+ } else if (breadcrumbs.length === 1) {
196
+ // Hide breadcrumbs if there is only one breadcrumb item
197
+ return "";
198
+ }
199
+
200
+ // remove the last breadcrumb item (the search result title makes it redundant)
201
+ breadcrumbs.pop();
202
+
203
+ return `<span class="breadcrumb">/ ${breadcrumbs.join(" / ")} /</span>`;
204
+ };
205
+
206
+ const buildTemplateHelperBreadcrumbs = (value: string) => {
207
+ const parts = value.split("/").filter((part) => part !== "");
208
+
209
+ // Don't show breadcrumbs if there's only one part
210
+ if (parts.length === 1) {
211
+ return "";
212
+ }
213
+
214
+ const breadcrumbs = processParts(parts);
215
+
216
+ // remove last breadcrumb item
217
+ breadcrumbs.pop();
218
+
219
+ return `
220
+ <span class="breadcrumb">/ ${breadcrumbs.join(" / ")} /</span>
221
+ `;
222
+ };
223
+
224
+ const buildTemplateHelperMetaBreadcrumbs = (value: string) => {
225
+ const parts = value.split("/").filter((part) => part !== "");
226
+
227
+ // Don't show breadcrumbs if there's only one part
228
+ if (parts.length === 1) {
229
+ return "";
230
+ }
231
+
232
+ const breadcrumbs = processParts(parts);
233
+
234
+ return `
235
+ <span class="breadcrumb">/ ${breadcrumbs.join(" / ")}</span>
236
+ `;
237
+ };
238
+
91
239
  // @ts-ignore Dark Magic (TM) we are overriding the 'title' field with a custom getter. We should really stop doing this.
92
240
  export default class SearchResults extends LightningElement {
93
241
  @api coveoOrganizationId!: string;
@@ -139,9 +287,12 @@ export default class SearchResults extends LightningElement {
139
287
  BreadcrumbManager.clearBreadcrumbs();
140
288
  }
141
289
 
142
- private currentPage: number = 1;
290
+ private currentPage: number = 25;
143
291
  private totalPages: number = 1;
144
292
 
293
+ private originalBreadcrumbs: string[] = [];
294
+ private initialWindowWidth = window.innerWidth;
295
+
145
296
  private goToPage(e: CustomEvent) {
146
297
  const page = e.detail;
147
298
  const Pager = Coveo.get(
@@ -179,6 +330,7 @@ export default class SearchResults extends LightningElement {
179
330
  if (Coveo.state(this.root!, "q") === "") {
180
331
  Coveo.state(this.root!, "sort", "date descending");
181
332
  }
333
+
182
334
  this.isInitialized = true;
183
335
  }
184
336
  );
@@ -204,6 +356,126 @@ export default class SearchResults extends LightningElement {
204
356
 
205
357
  this.trackSearchResults(event, this.query, this.totalResults);
206
358
  });
359
+
360
+ Coveo.$$(root).on(Coveo.QueryEvents.deferredQuerySuccess, async () => {
361
+ // wait specified time to ensure breadcrumbs are rendered before processing them
362
+ await pollUntil(
363
+ () => {
364
+ const coveoResults =
365
+ this.root!.querySelector(".CoveoResult");
366
+
367
+ return Boolean(coveoResults);
368
+ },
369
+ 20,
370
+ 1000
371
+ );
372
+
373
+ this.processBreadcrumbs(root);
374
+
375
+ window.onresize = () => this.processBreadcrumbs(root);
376
+ });
377
+ }
378
+
379
+ // check if the breadcrumb is overflowing or not based on the height of a single character in the text element
380
+ private isTextWrapping = (element: HTMLElement) => {
381
+ return element.offsetHeight > MAX_BREADCRUMB_HEIGHT;
382
+ };
383
+
384
+ private truncateBreadcrumbText = (breadcrumbItems: HTMLElement[]) => {
385
+ breadcrumbItems.forEach((breadcrumbItem: HTMLElement) => {
386
+ const breadcrumbItemText = breadcrumbItem.textContent!;
387
+ if (breadcrumbItemText.length > 30) {
388
+ breadcrumbItem.textContent = `${breadcrumbItemText.substring(
389
+ 0,
390
+ 30
391
+ )}...`;
392
+ }
393
+ });
394
+ };
395
+
396
+ private addBreadcrumbEllipsis = (
397
+ breadcrumbItems: HTMLElement[],
398
+ breadcrumb: HTMLElement
399
+ ) => {
400
+ for (let i = 1; i < breadcrumbItems.length; i++) {
401
+ if (this.isTextWrapping(breadcrumb)) {
402
+ // if the previous element is an ellipsis, make it empty (in order to avoid multiple grouped ellipsis)
403
+ if (breadcrumbItems[i - 1]?.textContent === "...") {
404
+ breadcrumbItems[i].innerHTML = "";
405
+ } else {
406
+ breadcrumbItems[i].textContent = "...";
407
+ }
408
+ } else {
409
+ break; // Exit the loop if the breadcrumb is no longer overflowing
410
+ }
411
+ }
412
+
413
+ // remove any empty breadcrumb items
414
+ breadcrumb.innerHTML = breadcrumb.innerHTML
415
+ .replace(
416
+ / ?\/ +<span class="breadcrumb-item"><\/span> +\/ ?/g,
417
+ " / "
418
+ )
419
+ // when first loading the page on mobile, the breadcrumb items are not grouped correctly
420
+ .replace(
421
+ `<span class="breadcrumb-item">...</span> / <span class="breadcrumb-item">...</span>`,
422
+ `<span class="breadcrumb-item">...</span>`
423
+ );
424
+ };
425
+
426
+ private formatBreadcrumbs = (breadcrumbs: HTMLElement[]) => {
427
+ breadcrumbs?.forEach((breadcrumb: HTMLElement) => {
428
+ // Get all breadcrumb items that are separated by '/'
429
+ const breadcrumbItems: any =
430
+ breadcrumb.querySelectorAll(".breadcrumb-item");
431
+
432
+ // Check if the breadcrumb is overflowing by comparing it's height to the height of the first breadcrumb item
433
+ if (this.isTextWrapping(breadcrumb)) {
434
+ // it is overflowing, so we need to truncate long titles to 30 characters
435
+ this.truncateBreadcrumbText(breadcrumbItems);
436
+
437
+ // Iteratively check if the breadcrumb is still overflowing and replace text with '...' starting from the second breadcrumb item
438
+ this.addBreadcrumbEllipsis(breadcrumbItems, breadcrumb);
439
+
440
+ // After processing all breadcrumb items, if it's still overflowing, hide the breadcrumb element
441
+ if (this.isTextWrapping(breadcrumb)) {
442
+ breadcrumb.style.display = "none";
443
+ }
444
+ }
445
+ });
446
+ };
447
+
448
+ private restoreBreadcrumbs = (breadcrumbs: HTMLElement[]) => {
449
+ breadcrumbs.forEach((breadcrumb: HTMLElement, index: number) => {
450
+ // eslint-disable-next-line @lwc/lwc/no-inner-html
451
+ breadcrumb.innerHTML = this.originalBreadcrumbs[index];
452
+ });
453
+ };
454
+
455
+ private windowSizeIncreased = () =>
456
+ window.innerWidth > this.initialWindowWidth;
457
+
458
+ private processBreadcrumbs(root: HTMLElement) {
459
+ // Get all breadcrumbs from search results
460
+ const breadcrumbs = Array.from(
461
+ root.querySelectorAll(".breadcrumb")
462
+ ) as HTMLElement[];
463
+
464
+ if (this.originalBreadcrumbs.length === 0) {
465
+ this.originalBreadcrumbs = breadcrumbs.map(
466
+ (breadcrumb) => breadcrumb.innerHTML
467
+ );
468
+ }
469
+
470
+ if (this.windowSizeIncreased()) {
471
+ /*
472
+ Reset the breadcrumbs to their original state and process them again.
473
+ The additional space means we can replace ellipsis with full text.
474
+ */
475
+ this.restoreBreadcrumbs(breadcrumbs);
476
+ }
477
+
478
+ this.formatBreadcrumbs(breadcrumbs);
207
479
  }
208
480
 
209
481
  private initializeCoveo() {
@@ -212,6 +484,7 @@ export default class SearchResults extends LightningElement {
212
484
  const resultsList = this.template.querySelector(".CoveoResultList");
213
485
 
214
486
  if (resultsList) {
487
+ // eslint-disable-next-line @lwc/lwc/no-inner-html
215
488
  resultsList.innerHTML = resultsTemplatesInnerHtml;
216
489
  }
217
490
 
@@ -227,6 +500,21 @@ export default class SearchResults extends LightningElement {
227
500
  buildTemplateHelperBadge
228
501
  );
229
502
 
503
+ Coveo.TemplateHelpers.registerTemplateHelper(
504
+ "breadcrumbs",
505
+ buildTemplateHelperBreadcrumbs
506
+ );
507
+
508
+ Coveo.TemplateHelpers.registerTemplateHelper(
509
+ "metabreadcrumbs",
510
+ buildTemplateHelperMetaBreadcrumbs
511
+ );
512
+
513
+ Coveo.TemplateHelpers.registerTemplateHelper(
514
+ "uriBreadcrumbs",
515
+ buildTemplateHelperUriBreadcrumbs
516
+ );
517
+
230
518
  Coveo.init(this.root);
231
519
  }
232
520
 
@@ -235,6 +523,7 @@ export default class SearchResults extends LightningElement {
235
523
  if (Object.prototype.hasOwnProperty.call(window, "Coveo")) {
236
524
  this.initializeCoveo();
237
525
  } else {
526
+ // eslint-disable-next-line @lwc/lwc/no-document-query
238
527
  const script = document.querySelector("script.coveo-script");
239
528
  script?.addEventListener("load", () => {
240
529
  this.initializeCoveo();
@@ -15,8 +15,7 @@ dx-empty-state {
15
15
 
16
16
  .sidebar-content {
17
17
  overflow-y: auto;
18
- border-top: 1px solid var(--dx-g-gray-90);
19
- padding-bottom: var(--dx-g-spacing-md);
18
+ padding: var(--dx-g-spacing-sm) 0 var(--dx-g-spacing-md);
20
19
  }
21
20
 
22
21
  .loading-skeleton {
@@ -54,6 +53,13 @@ dx-empty-state {
54
53
  --dx-c-button-secondary-color-hover: var(--dx-g-gray-80);
55
54
  }
56
55
 
56
+ .results-heading {
57
+ --dx-g-text-body-color: var(--sds-g-gray-14);
58
+
59
+ margin-bottom: var(--dx-g-spacing-sm);
60
+ font-weight: var(--dx-g-font-bold);
61
+ }
62
+
57
63
  @media (max-width: 768px) {
58
64
  :host {
59
65
  width: 100%;
@@ -62,8 +68,4 @@ dx-empty-state {
62
68
  .loading-skeleton {
63
69
  width: 100%;
64
70
  }
65
-
66
- .sidebar-content-tree {
67
- padding: 0 var(--dx-g-spacing-sm);
68
- }
69
71
  }
@@ -22,7 +22,10 @@
22
22
  </div>
23
23
  </div>
24
24
 
25
- <div class="sidebar-header padding-horizontal">
25
+ <div
26
+ class="sidebar-header padding-horizontal"
27
+ show-shadow={showBoxShadow}
28
+ >
26
29
  <div class="header" if:false={mobile}>
27
30
  <h2 class="dx-text-display-6 header-title">{header}</h2>
28
31
  <slot name="header"></slot>
@@ -42,7 +45,7 @@
42
45
  ></dx-sidebar-search>
43
46
  </div>
44
47
  <h2
45
- class="results-heading dx-text-display-8"
48
+ class="results-heading dx-text-body-3"
46
49
  if:true={showResultsHeading}
47
50
  >
48
51
  Results
@@ -83,7 +86,7 @@
83
86
  size="small"
84
87
  ></dx-empty-state>
85
88
  </div>
86
- <div class="sidebar-content sidebar-content-tree">
89
+ <div class={sidebarContentClass} onscroll={handleScroll}>
87
90
  <dx-tree
88
91
  for:each={trees}
89
92
  for:item="tree"
@@ -175,7 +175,7 @@ export default class Sidebar extends SidebarBase {
175
175
  this.isSearchLoading = e.detail;
176
176
  }
177
177
 
178
- private onSidebarSearchContentScroll() {
178
+ private onSidebarSearchContentScroll(scrollEvent) {
179
179
  if (this.isSearchLoading) {
180
180
  return;
181
181
  }
@@ -192,6 +192,7 @@ export default class Sidebar extends SidebarBase {
192
192
  this.requestedFetchMoreResults = true;
193
193
  search.fetchMoreResults();
194
194
  }
195
+ this.handleScroll(scrollEvent);
195
196
  }
196
197
 
197
198
  private initializeSearchScrollPosition() {
@@ -2,7 +2,7 @@
2
2
 
3
3
  .sidebar-content {
4
4
  overflow-y: auto;
5
- border-top: 1px solid var(--dx-g-gray-90);
5
+ padding-top: var(--dx-g-spacing-sm);
6
6
  }
7
7
 
8
8
  dx-empty-state {