@salesforcedevs/dx-components 1.3.204 → 1.3.206-alpha.11
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.
|
|
3
|
+
"version": "1.3.206-alpha.11",
|
|
4
4
|
"description": "DX Lightning web components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"engines": {
|
|
@@ -43,6 +43,5 @@
|
|
|
43
43
|
},
|
|
44
44
|
"volta": {
|
|
45
45
|
"node": "16.19.1"
|
|
46
|
-
}
|
|
47
|
-
"gitHead": "d424645944682ab94a53cad2c0994ad4f739824b"
|
|
46
|
+
}
|
|
48
47
|
}
|
|
@@ -386,7 +386,13 @@ a.CoveoResultLink,
|
|
|
386
386
|
width: fit-content;
|
|
387
387
|
}
|
|
388
388
|
|
|
389
|
-
.dx-
|
|
390
|
-
display:
|
|
389
|
+
.dx-result-info {
|
|
390
|
+
display: flex;
|
|
391
|
+
gap: 12px;
|
|
391
392
|
margin-bottom: var(--dx-g-spacing-smd);
|
|
392
393
|
}
|
|
394
|
+
|
|
395
|
+
.breadcrumb {
|
|
396
|
+
color: #555;
|
|
397
|
+
font-size: var(--dx-g-text-xs);
|
|
398
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { LightningElement, api, track } from "lwc";
|
|
2
2
|
import type * as CoveoSDK from "coveo-search-ui";
|
|
3
|
+
import debounce from "debounce";
|
|
3
4
|
import { track as trackGTM } from "dxUtils/analytics";
|
|
4
5
|
import {
|
|
5
6
|
CONTENT_TYPE_LABELS,
|
|
@@ -44,7 +45,11 @@ const resultsTemplatesInnerHtml = `
|
|
|
44
45
|
data-field-publicurl=""
|
|
45
46
|
>
|
|
46
47
|
<div class="dx-result">
|
|
47
|
-
<
|
|
48
|
+
<div class="dx-result-info">
|
|
49
|
+
<span class="CoveoFieldValue" data-field="@content_type" data-helper="badge" data-html-value="true"></span>
|
|
50
|
+
<span class="CoveoFieldValue" data-field="@uri" data-helper="uriBreadcrumbs" data-html-value="true"></span>
|
|
51
|
+
<span class="CoveoFieldValue" data-field="@breadcrumbs" data-helper="breadcrumbs" data-html-value="true"></span>
|
|
52
|
+
</div>
|
|
48
53
|
<p class="dx-result-title">
|
|
49
54
|
<a
|
|
50
55
|
class="CoveoResultLink"
|
|
@@ -60,7 +65,11 @@ const resultsTemplatesInnerHtml = `
|
|
|
60
65
|
type="text/html"
|
|
61
66
|
>
|
|
62
67
|
<div class="dx-result">
|
|
63
|
-
<
|
|
68
|
+
<div class="dx-result-info">
|
|
69
|
+
<span class="CoveoFieldValue" data-field="@content_type" data-helper="badge" data-html-value="true"></span>
|
|
70
|
+
<span class="CoveoFieldValue" data-field="@uri" data-helper="uriBreadcrumbs" data-html-value="true"></span>
|
|
71
|
+
<span class="CoveoFieldValue" data-field="@breadcrumbs" data-helper="breadcrumbs" data-html-value="true"></span>
|
|
72
|
+
</div>
|
|
64
73
|
<p class="dx-result-title">
|
|
65
74
|
<a class="CoveoResultLink"></a>
|
|
66
75
|
</p>
|
|
@@ -88,6 +97,83 @@ const buildTemplateHelperBadge = (value: keyof typeof CONTENT_TYPE_LABELS) => {
|
|
|
88
97
|
`;
|
|
89
98
|
};
|
|
90
99
|
|
|
100
|
+
const processParts = (parts: string[]) => {
|
|
101
|
+
return parts.map((part) => {
|
|
102
|
+
// Remove special characters & .htm/.xml extension
|
|
103
|
+
part = part
|
|
104
|
+
.replace(/_/g, " ")
|
|
105
|
+
.replace(/-/g, " ")
|
|
106
|
+
.replace(/.htm/g, " ")
|
|
107
|
+
.replace(/.xml/g, " ");
|
|
108
|
+
|
|
109
|
+
// Capitalize first letter of each word
|
|
110
|
+
part = part.replace(/\w\S*/g, (w) => {
|
|
111
|
+
return w.replace(/^\w/, (c) => c.toUpperCase());
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return `<span class="breadcrumb-item">${decodeURI(part)}</span>`;
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const buildTemplateHelperUriBreadcrumbs = (value: string) => {
|
|
119
|
+
const url = new URL(value);
|
|
120
|
+
|
|
121
|
+
// Only generate breadcrumbs for external links using the URL
|
|
122
|
+
const hostnamePattern =
|
|
123
|
+
/^((www\.)?developer\.salesforce\.com|(www\.)?developer-website-s\.herokuapp\.com|(www\.)?youtube\.com)$/;
|
|
124
|
+
if (hostnamePattern.test(url.hostname)) {
|
|
125
|
+
return "";
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let parts = url.pathname.split("/").filter((part) => part !== "");
|
|
129
|
+
|
|
130
|
+
// Remove language prefix from trailhead URLs
|
|
131
|
+
if (
|
|
132
|
+
url.hostname === "trailhead.salesforce.com" ||
|
|
133
|
+
url.hostname === "dev.trailhead.salesforce.com"
|
|
134
|
+
) {
|
|
135
|
+
parts = parts
|
|
136
|
+
.slice(1)
|
|
137
|
+
.filter((part) => part !== "content" && part !== "learn");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const breadcrumbs = processParts(parts);
|
|
141
|
+
|
|
142
|
+
breadcrumbs.unshift(`<span class="breadcrumb-item">${url.hostname}</span>`);
|
|
143
|
+
|
|
144
|
+
return `
|
|
145
|
+
<span class="breadcrumb">
|
|
146
|
+
${breadcrumbs.join(" / ")}
|
|
147
|
+
</span>
|
|
148
|
+
`;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const buildTemplateHelperBreadcrumbs = (value: string) => {
|
|
152
|
+
let parts: string[] = [];
|
|
153
|
+
|
|
154
|
+
if (value.includes("developer.salesforce.com")) {
|
|
155
|
+
const url = new URL(value);
|
|
156
|
+
parts = url.pathname.split("/").filter((part) => part !== "");
|
|
157
|
+
} else {
|
|
158
|
+
parts = value.split("/").filter((part) => part !== "");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Don't show breadcrumbs if there's only one part or two (two because we remove the last item in the next step)
|
|
162
|
+
if (parts.length === 1 || parts.length === 2) {
|
|
163
|
+
return "";
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const breadcrumbs = processParts(parts);
|
|
167
|
+
|
|
168
|
+
// Remove the last breadcrumb item
|
|
169
|
+
breadcrumbs.pop();
|
|
170
|
+
console.log(breadcrumbs);
|
|
171
|
+
|
|
172
|
+
return `
|
|
173
|
+
<span class="breadcrumb">/ ${breadcrumbs.join(" / ")} /</span>
|
|
174
|
+
`;
|
|
175
|
+
};
|
|
176
|
+
|
|
91
177
|
// @ts-ignore Dark Magic (TM) we are overriding the 'title' field with a custom getter. We should really stop doing this.
|
|
92
178
|
export default class SearchResults extends LightningElement {
|
|
93
179
|
@api coveoOrganizationId!: string;
|
|
@@ -134,9 +220,12 @@ export default class SearchResults extends LightningElement {
|
|
|
134
220
|
BreadcrumbManager.clearBreadcrumbs();
|
|
135
221
|
}
|
|
136
222
|
|
|
137
|
-
private currentPage: number =
|
|
223
|
+
private currentPage: number = 25;
|
|
138
224
|
private totalPages: number = 1;
|
|
139
225
|
|
|
226
|
+
private originalBreadcrumbs: string[] = [];
|
|
227
|
+
private windowWidth = window.innerWidth;
|
|
228
|
+
|
|
140
229
|
private goToPage(e: CustomEvent) {
|
|
141
230
|
const page = e.detail;
|
|
142
231
|
const Pager = Coveo.get(
|
|
@@ -174,6 +263,7 @@ export default class SearchResults extends LightningElement {
|
|
|
174
263
|
if (Coveo.state(this.root!, "q") === "") {
|
|
175
264
|
Coveo.state(this.root!, "sort", "date descending");
|
|
176
265
|
}
|
|
266
|
+
|
|
177
267
|
this.isInitialized = true;
|
|
178
268
|
}
|
|
179
269
|
);
|
|
@@ -193,6 +283,98 @@ export default class SearchResults extends LightningElement {
|
|
|
193
283
|
});
|
|
194
284
|
this.trackSearchResults(event, this.query, this.totalResults);
|
|
195
285
|
});
|
|
286
|
+
|
|
287
|
+
Coveo.$$(root).on(Coveo.QueryEvents.deferredQuerySuccess, () => {
|
|
288
|
+
this.processBreadcrumbs(root);
|
|
289
|
+
|
|
290
|
+
window.onresize = debounce(() => {
|
|
291
|
+
this.processBreadcrumbs(root);
|
|
292
|
+
}, 10);
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Checks if text is wrapping by comparing it with an element's text that doesn't wrap
|
|
297
|
+
private isTextWrapping = (
|
|
298
|
+
elementOne: HTMLElement,
|
|
299
|
+
elementTwo: HTMLElement
|
|
300
|
+
) => elementOne.offsetHeight > elementTwo.offsetHeight;
|
|
301
|
+
|
|
302
|
+
private truncateBreadcrumbText = (breadcrumbItems: HTMLElement[]) => {
|
|
303
|
+
breadcrumbItems.forEach((breadcrumbItem: HTMLElement) => {
|
|
304
|
+
const breadcrumbItemText = breadcrumbItem.textContent!;
|
|
305
|
+
if (breadcrumbItemText.length > 30) {
|
|
306
|
+
breadcrumbItem.textContent = `${breadcrumbItemText.substring(
|
|
307
|
+
0,
|
|
308
|
+
30
|
|
309
|
+
)}...`;
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
private addBreadcrumbEllipsis = (
|
|
315
|
+
breadcrumbItems: HTMLElement[],
|
|
316
|
+
breadcrumb: HTMLElement
|
|
317
|
+
) => {
|
|
318
|
+
for (let i = 1; i < breadcrumbItems.length; i++) {
|
|
319
|
+
if (this.isTextWrapping(breadcrumb, breadcrumbItems[0])) {
|
|
320
|
+
breadcrumbItems[i].textContent = "...";
|
|
321
|
+
} else {
|
|
322
|
+
break; // Exit the loop if the breadcrumb is no longer overflowing
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
private formatBreadcrumbs = (breadcrumbs: HTMLElement[]) => {
|
|
328
|
+
breadcrumbs?.forEach((breadcrumb: HTMLElement) => {
|
|
329
|
+
// Get all breadcrumb items that are separated by '/'
|
|
330
|
+
const breadcrumbItems: any =
|
|
331
|
+
breadcrumb.querySelectorAll(".breadcrumb-item");
|
|
332
|
+
|
|
333
|
+
// Check if the breadcrumb is overflowing by comparing it's height to the height of the first breadcrumb item
|
|
334
|
+
if (this.isTextWrapping(breadcrumb, breadcrumbItems[0])) {
|
|
335
|
+
// it is overflowing, so we need to truncate long titles to 30 characters
|
|
336
|
+
this.truncateBreadcrumbText(breadcrumbItems);
|
|
337
|
+
|
|
338
|
+
// Iteratively check if the breadcrumb is still overflowing and replace text with '...' starting from the second breadcrumb item
|
|
339
|
+
this.addBreadcrumbEllipsis(breadcrumbItems, breadcrumb);
|
|
340
|
+
|
|
341
|
+
// After processing all breadcrumb items, if it's still overflowing, hide the breadcrumb element
|
|
342
|
+
if (this.isTextWrapping(breadcrumb, breadcrumbItems[0])) {
|
|
343
|
+
breadcrumb.style.display = "none";
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
private restoreBreadcrumbs = (breadcrumbs: HTMLElement[]) => {
|
|
350
|
+
breadcrumbs.forEach((breadcrumb: HTMLElement, index: number) => {
|
|
351
|
+
// eslint-disable-next-line @lwc/lwc/no-inner-html
|
|
352
|
+
breadcrumb.innerHTML = this.originalBreadcrumbs[index];
|
|
353
|
+
});
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
private windowSizeIncreased = () => window.innerWidth > this.windowWidth;
|
|
357
|
+
|
|
358
|
+
private processBreadcrumbs(root: HTMLElement) {
|
|
359
|
+
// Get all breadcrumbs from search results
|
|
360
|
+
const breadcrumbs = root.querySelectorAll(
|
|
361
|
+
".breadcrumb"
|
|
362
|
+
// eslint-disable-next-line no-undef
|
|
363
|
+
) as NodeListOf<HTMLElement>;
|
|
364
|
+
|
|
365
|
+
if (this.originalBreadcrumbs.length === 0) {
|
|
366
|
+
this.originalBreadcrumbs = Array.from(breadcrumbs).map(
|
|
367
|
+
// eslint-disable-next-line @lwc/lwc/no-inner-html
|
|
368
|
+
(breadcrumb) => breadcrumb.innerHTML
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (this.windowSizeIncreased()) {
|
|
373
|
+
// reset the breadcrumbs to their original state
|
|
374
|
+
this.restoreBreadcrumbs(Array.from(breadcrumbs));
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
this.formatBreadcrumbs(Array.from(breadcrumbs));
|
|
196
378
|
}
|
|
197
379
|
|
|
198
380
|
private initializeCoveo() {
|
|
@@ -200,6 +382,7 @@ export default class SearchResults extends LightningElement {
|
|
|
200
382
|
|
|
201
383
|
const resultsList = this.template.querySelector(".CoveoResultList");
|
|
202
384
|
if (resultsList) {
|
|
385
|
+
// eslint-disable-next-line @lwc/lwc/no-inner-html
|
|
203
386
|
resultsList.innerHTML = resultsTemplatesInnerHtml;
|
|
204
387
|
}
|
|
205
388
|
|
|
@@ -215,6 +398,16 @@ export default class SearchResults extends LightningElement {
|
|
|
215
398
|
buildTemplateHelperBadge
|
|
216
399
|
);
|
|
217
400
|
|
|
401
|
+
Coveo.TemplateHelpers.registerTemplateHelper(
|
|
402
|
+
"uriBreadcrumbs",
|
|
403
|
+
buildTemplateHelperUriBreadcrumbs
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
Coveo.TemplateHelpers.registerTemplateHelper(
|
|
407
|
+
"breadcrumbs",
|
|
408
|
+
buildTemplateHelperBreadcrumbs
|
|
409
|
+
);
|
|
410
|
+
|
|
218
411
|
Coveo.init(this.root);
|
|
219
412
|
}
|
|
220
413
|
|
|
@@ -223,6 +416,7 @@ export default class SearchResults extends LightningElement {
|
|
|
223
416
|
if (Object.prototype.hasOwnProperty.call(window, "Coveo")) {
|
|
224
417
|
this.initializeCoveo();
|
|
225
418
|
} else {
|
|
419
|
+
// eslint-disable-next-line @lwc/lwc/no-document-query
|
|
226
420
|
const script = document.querySelector("script.coveo-script");
|
|
227
421
|
script?.addEventListener("load", () => {
|
|
228
422
|
this.initializeCoveo();
|
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.
|