@salesforcedevs/dx-components 1.30.6 → 1.32.0-alpha.1
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/LICENSE +12 -0
- package/lwc.config.json +2 -0
- package/package.json +3 -2
- package/src/modules/dx/dropdownOption/dropdownOption.css +13 -8
- package/src/modules/dx/globalHeader/globalHeader.html +5 -0
- package/src/modules/dx/globalHeader/globalHeader.ts +76 -0
- package/src/modules/dx/searchResults/coveo.css +18001 -0
- package/src/modules/dx/searchResults/searchResults.css +105 -6
- package/src/modules/dx/searchResults/searchResults.html +84 -91
- package/src/modules/dx/searchResults/searchResults.ts +157 -478
- package/src/modules/dx/sidebar/sidebar.css +1 -0
- package/src/modules/dx/sidebar/sidebar.html +1 -5
- package/src/modules/dx/sidebar/sidebar.ts +7 -7
- package/src/modules/dx/sidebarSearch/sidebarSearch.ts +142 -344
- package/src/modules/dx/sidebarSearchResult/sidebarSearchResult.css +41 -21
- package/src/modules/dx/sidebarSearchResult/sidebarSearchResult.ts +4 -4
- package/src/modules/dx/tooltip/microtip.css +269 -0
- package/src/modules/dxUtils/data360Search/data360Search.ts +168 -0
|
@@ -13,6 +13,7 @@ dx-empty-state {
|
|
|
13
13
|
display: none;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/* Scrollable list: when content overflows, last visible item is cut by container to indicate more items (per spec). */
|
|
16
17
|
.sidebar-content {
|
|
17
18
|
overflow-y: auto;
|
|
18
19
|
padding: var(--dx-g-spacing-sm) 0 var(--dx-g-spacing-2xl);
|
|
@@ -48,10 +48,6 @@
|
|
|
48
48
|
<dx-sidebar-search
|
|
49
49
|
onchange={onSearchChange}
|
|
50
50
|
onloading={onSearchLoading}
|
|
51
|
-
coveo-organization-id={coveoOrganizationId}
|
|
52
|
-
coveo-public-access-token={coveoPublicAccessToken}
|
|
53
|
-
coveo-search-hub={coveoSearchHub}
|
|
54
|
-
coveo-advanced-query-config={coveoAdvancedQueryConfig}
|
|
55
51
|
></dx-sidebar-search>
|
|
56
52
|
</div>
|
|
57
53
|
<slot name="version-picker"></slot>
|
|
@@ -69,7 +65,7 @@
|
|
|
69
65
|
<img
|
|
70
66
|
lwc:if={isSearchLoading}
|
|
71
67
|
class="loading-skeleton padding-horizontal"
|
|
72
|
-
src="https://
|
|
68
|
+
src="https://a.sfdcstatic.com/developer-website/prod/images/sidebar-loading.svg"
|
|
73
69
|
alt="loading"
|
|
74
70
|
/>
|
|
75
71
|
<template
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import cx from "classnames";
|
|
2
2
|
import { api, track } from "lwc";
|
|
3
3
|
import { TreeNode, SidebarSearchResult } from "typings/custom";
|
|
4
|
-
import { getSidebarSearchParams } from "dxUtils/coveo";
|
|
5
4
|
import { toJson } from "dxUtils/normalizers";
|
|
6
5
|
import SidebarSearch from "dx/sidebarSearch";
|
|
7
6
|
import { SidebarBase } from "dxBaseElements/sidebarBase";
|
|
8
7
|
|
|
9
8
|
const MOBILE_SIZE_MATCH = "768px";
|
|
10
9
|
|
|
10
|
+
const getSearchQueryParam = (): string | null =>
|
|
11
|
+
typeof window !== "undefined"
|
|
12
|
+
? new URLSearchParams(window.location.search).get("q")
|
|
13
|
+
: null;
|
|
14
|
+
|
|
11
15
|
export default class Sidebar extends SidebarBase {
|
|
12
|
-
@api coveoOrganizationId!: string;
|
|
13
|
-
@api coveoPublicAccessToken!: string;
|
|
14
|
-
@api coveoSearchHub!: string;
|
|
15
|
-
@api coveoAdvancedQueryConfig!: string;
|
|
16
16
|
@api header: string = "";
|
|
17
17
|
|
|
18
18
|
@api
|
|
@@ -130,7 +130,7 @@ export default class Sidebar extends SidebarBase {
|
|
|
130
130
|
constructor() {
|
|
131
131
|
super();
|
|
132
132
|
|
|
133
|
-
if (
|
|
133
|
+
if (getSearchQueryParam()) {
|
|
134
134
|
this.isSearchLoading = true;
|
|
135
135
|
this.scrollToSelectedSearchResult = true;
|
|
136
136
|
}
|
|
@@ -175,7 +175,7 @@ export default class Sidebar extends SidebarBase {
|
|
|
175
175
|
this.isSearchLoading = e.detail;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
private onSidebarSearchContentScroll(scrollEvent) {
|
|
178
|
+
private onSidebarSearchContentScroll(scrollEvent: Event) {
|
|
179
179
|
if (this.isSearchLoading) {
|
|
180
180
|
return;
|
|
181
181
|
}
|
|
@@ -1,75 +1,70 @@
|
|
|
1
1
|
import { LightningElement, api } from "lwc";
|
|
2
2
|
import debounce from "debounce";
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
loadAdvancedSearchQueryActions,
|
|
14
|
-
SearchEngine,
|
|
15
|
-
SearchAppState,
|
|
16
|
-
loadQueryActions
|
|
17
|
-
} from "@coveo/headless";
|
|
18
|
-
import { buildSearchEngine, getSidebarSearchParams } from "dxUtils/coveo";
|
|
4
|
+
type Data360SearchCacheItem,
|
|
5
|
+
type Data360SearchResultItem,
|
|
6
|
+
fetchSearch,
|
|
7
|
+
getBaseUrlPath,
|
|
8
|
+
getStoredSearch,
|
|
9
|
+
isCacheStale,
|
|
10
|
+
setStoredSearch
|
|
11
|
+
} from "dxUtils/data360Search";
|
|
12
|
+
import { createSearchRegExp } from "dxUtils/regexps";
|
|
19
13
|
import { RecentSearches } from "dxUtils/recentSearches";
|
|
20
|
-
import { toJson } from "dxUtils/normalizers";
|
|
21
14
|
import {
|
|
15
|
+
type HighlightedSections,
|
|
22
16
|
Option,
|
|
23
17
|
PopoverRequestCloseType,
|
|
24
18
|
SidebarSearchResult
|
|
25
19
|
} from "typings/custom";
|
|
26
20
|
|
|
27
|
-
const RESULT_CLICK_LOCAL_STORAGE_KEY = "dx-sidebar-search-result-click";
|
|
28
|
-
export const SESSION_KEY = "dx-sidebar-search-state";
|
|
29
|
-
|
|
30
|
-
const DEFAULT_RESULT_COUNT = 20;
|
|
31
21
|
const SEARCH_DEBOUNCE_DELAY = 1200;
|
|
32
22
|
|
|
33
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Find all matches of the search query in text and return ranges for highlighting.
|
|
25
|
+
* Uses the same regex as the full-doc highlighter (createSearchRegExp) so sidebar
|
|
26
|
+
* and doc highlight the same phrases (including spaces between words).
|
|
27
|
+
*/
|
|
28
|
+
function getHighlightRanges(
|
|
29
|
+
text: string,
|
|
30
|
+
searchQuery: string
|
|
31
|
+
): HighlightedSections {
|
|
32
|
+
if (!text || !searchQuery.trim()) {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
const re = createSearchRegExp(searchQuery.trim());
|
|
36
|
+
const ranges: HighlightedSections = [];
|
|
37
|
+
let match: RegExpExecArray | null;
|
|
38
|
+
while ((match = re.exec(text)) !== null) {
|
|
39
|
+
ranges.push({ offset: match.index, length: match[0].length });
|
|
40
|
+
}
|
|
41
|
+
return ranges;
|
|
42
|
+
}
|
|
34
43
|
|
|
35
|
-
const
|
|
36
|
-
version?.split(".").join("\\.");
|
|
44
|
+
const UserRecentSearches = new RecentSearches();
|
|
37
45
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
@api coveoSearchHub!: string;
|
|
42
|
-
@api
|
|
43
|
-
get coveoAdvancedQueryConfig(): { [key: string]: any } {
|
|
44
|
-
return this._coveoAdvancedQueryConfig;
|
|
45
|
-
}
|
|
46
|
-
set coveoAdvancedQueryConfig(value) {
|
|
47
|
-
const jsonValue = toJson(value);
|
|
48
|
-
const hasChanged =
|
|
49
|
-
this._coveoAdvancedQueryConfig &&
|
|
50
|
-
Object.entries(jsonValue).some(
|
|
51
|
-
([key, val]: [string, any]) =>
|
|
52
|
-
this._coveoAdvancedQueryConfig[key] !== val
|
|
53
|
-
);
|
|
54
|
-
this._coveoAdvancedQueryConfig = jsonValue;
|
|
46
|
+
function resultPathFromHref(href: string): string {
|
|
47
|
+
return href.startsWith("http") ? new URL(href).pathname : href;
|
|
48
|
+
}
|
|
55
49
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
function applySelectedAndSelect(
|
|
51
|
+
items: Data360SearchCacheItem[]
|
|
52
|
+
): SidebarSearchResult[] {
|
|
53
|
+
const currentPath = window.location.pathname;
|
|
54
|
+
return items.map((item) => ({
|
|
55
|
+
...item,
|
|
56
|
+
selected: !!item.href && resultPathFromHref(item.href) === currentPath,
|
|
57
|
+
select: () => {}
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
this.setAdvancedFilters(this.engine);
|
|
63
|
-
this.submitSearch(true);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
61
|
+
const getSearchQueryParam = (): string =>
|
|
62
|
+
new URLSearchParams(window.location.search).get("q") ?? "";
|
|
66
63
|
|
|
64
|
+
export default class SidebarSearch extends LightningElement {
|
|
67
65
|
@api
|
|
68
66
|
public fetchMoreResults() {
|
|
69
|
-
|
|
70
|
-
this.fetchingMoreResults = true;
|
|
71
|
-
this.resultList.fetchMoreResults();
|
|
72
|
-
}
|
|
67
|
+
// Data 360 Search API does not expose pagination in the same way; no-op
|
|
73
68
|
}
|
|
74
69
|
|
|
75
70
|
@api
|
|
@@ -85,313 +80,128 @@ export default class SidebarSearch extends LightningElement {
|
|
|
85
80
|
this.dropdownRequestedOpen = value;
|
|
86
81
|
}
|
|
87
82
|
|
|
88
|
-
private _coveoAdvancedQueryConfig!: { [key: string]: any };
|
|
89
83
|
private dropdownRequestedOpen: boolean = false;
|
|
90
84
|
private recentSearches: Option[] = [];
|
|
91
|
-
private
|
|
92
|
-
private resultList: ResultList | null = null;
|
|
93
|
-
private resultsPerPage: ResultsPerPage | null = null;
|
|
94
|
-
private preloadedState?: SearchAppState;
|
|
95
|
-
private searchBox: SearchBox | null = null;
|
|
96
|
-
private value?: string = "";
|
|
97
|
-
private engine: SearchEngine | null = null;
|
|
98
|
-
private fetchingMoreResults: boolean = false;
|
|
85
|
+
private value: string = "";
|
|
99
86
|
private didRender = false;
|
|
100
|
-
|
|
101
|
-
private get hasCorrectCoveoConfig(): boolean {
|
|
102
|
-
const coveoConfigurations = [
|
|
103
|
-
this.coveoOrganizationId,
|
|
104
|
-
this.coveoPublicAccessToken,
|
|
105
|
-
this.coveoSearchHub,
|
|
106
|
-
this.coveoAdvancedQueryConfig
|
|
107
|
-
];
|
|
108
|
-
|
|
109
|
-
return coveoConfigurations.every(
|
|
110
|
-
(val) =>
|
|
111
|
-
!!val && (typeof val === "string" || typeof val === "object")
|
|
112
|
-
);
|
|
113
|
-
}
|
|
87
|
+
private data360SearchInitialized: boolean = false;
|
|
114
88
|
|
|
115
89
|
private get isDropdownOpen() {
|
|
116
90
|
return (
|
|
117
91
|
this.dropdownRequestedOpen &&
|
|
118
|
-
this.recentSearches &&
|
|
119
|
-
this.recentSearches.length > 0 &&
|
|
92
|
+
this.recentSearches?.length > 0 &&
|
|
120
93
|
!this.value
|
|
121
94
|
);
|
|
122
95
|
}
|
|
123
96
|
|
|
124
97
|
constructor() {
|
|
125
98
|
super();
|
|
126
|
-
|
|
127
99
|
this.updateRecentSearches();
|
|
128
|
-
this.value =
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
disconnectedCallback() {
|
|
132
|
-
this.unsubscribeFromResultList();
|
|
100
|
+
this.value = getSearchQueryParam();
|
|
133
101
|
}
|
|
134
102
|
|
|
135
103
|
renderedCallback() {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (this.hasCorrectCoveoConfig && !this.engine) {
|
|
140
|
-
this.initializeUserSession();
|
|
141
|
-
this.initializeCoveo();
|
|
142
|
-
} else if (!this.hasCorrectCoveoConfig) {
|
|
143
|
-
console.error(
|
|
144
|
-
"Incorrect coveo search configuration provided. All coveo configuration attributes must be defined.",
|
|
145
|
-
{
|
|
146
|
-
coveoOrganizationId: this.coveoOrganizationId,
|
|
147
|
-
coveoPublicAccessToken: this.coveoPublicAccessToken,
|
|
148
|
-
coveoSearchHub: this.coveoSearchHub,
|
|
149
|
-
coveoAdvancedQueryConfig: this.coveoAdvancedQueryConfig
|
|
150
|
-
}
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// If this is the first render and there is a search value, trigger
|
|
155
|
-
// term highlighting
|
|
156
|
-
if (!this.didRender && this.value) {
|
|
157
|
-
this.dispatchHighlightedTermChange();
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
this.didRender = true;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
private initializeUserSession() {
|
|
164
|
-
const stringified = window.sessionStorage.getItem(SESSION_KEY);
|
|
165
|
-
|
|
166
|
-
if (!stringified) {
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
try {
|
|
171
|
-
const json = JSON.parse(stringified);
|
|
172
|
-
|
|
173
|
-
const hasSameConfig = Object.entries(
|
|
174
|
-
json.coveoAdvancedQueryConfig
|
|
175
|
-
).every(
|
|
176
|
-
([key, value]: [string, any]) =>
|
|
177
|
-
this.coveoAdvancedQueryConfig[key] === value
|
|
178
|
-
);
|
|
179
|
-
const hasSameQuery = json?.state?.query?.q === this.value;
|
|
180
|
-
|
|
181
|
-
if (hasSameConfig && hasSameQuery) {
|
|
182
|
-
this.preloadedState = json?.state;
|
|
183
|
-
} else {
|
|
184
|
-
window.sessionStorage.removeItem(SESSION_KEY);
|
|
185
|
-
}
|
|
186
|
-
} catch (e) {
|
|
187
|
-
console.error(e);
|
|
188
|
-
window.sessionStorage.removeItem(SESSION_KEY);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
private initializeCoveo = () => {
|
|
193
|
-
try {
|
|
194
|
-
this.engine = buildSearchEngine({
|
|
195
|
-
organizationId: this.coveoOrganizationId,
|
|
196
|
-
publicAccessToken: this.coveoPublicAccessToken,
|
|
197
|
-
searchHub: this.coveoSearchHub,
|
|
198
|
-
preloadedState: this.preloadedState
|
|
199
|
-
});
|
|
200
|
-
} catch (ex) {
|
|
201
|
-
console.error(`Coveo engine failed to initialize (${ex})`);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (!this.engine) {
|
|
205
|
-
console.error("Coveo engine failed to initialize!");
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
if (this.preloadedState) {
|
|
210
|
-
const actions = loadQueryActions(this.engine);
|
|
211
|
-
this.engine.dispatch(actions.updateQuery({ q: this.value || "" }));
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
this.resultList = buildResultList(this.engine, {
|
|
215
|
-
options: { fieldsToInclude: ["sfhtml_url__c"] }
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
this.resultsPerPage = buildResultsPerPage(this.engine, {
|
|
219
|
-
initialState: {
|
|
220
|
-
numberOfResults: DEFAULT_RESULT_COUNT
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
this.searchBox = buildSearchBox(this.engine, {
|
|
225
|
-
options: { numberOfSuggestions: 3 }
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
this.setAdvancedFilters(this.engine);
|
|
229
|
-
|
|
230
|
-
this.unsubscribeFromResultList = this.resultList?.subscribe(
|
|
231
|
-
this.onResultListChange
|
|
232
|
-
);
|
|
233
|
-
|
|
234
|
-
if (this.value) {
|
|
235
|
-
this.dispatchHighlightedTermChange();
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (!this.preloadedState) {
|
|
104
|
+
if (!this.data360SearchInitialized) {
|
|
105
|
+
this.data360SearchInitialized = true;
|
|
239
106
|
if (this.value) {
|
|
240
|
-
|
|
107
|
+
const baseUrlPath = getBaseUrlPath();
|
|
108
|
+
const cached = getStoredSearch(baseUrlPath);
|
|
109
|
+
const queryTrimmed = this.value.trim();
|
|
110
|
+
if (
|
|
111
|
+
cached?.query === queryTrimmed &&
|
|
112
|
+
Array.isArray(cached.results) &&
|
|
113
|
+
!isCacheStale(cached)
|
|
114
|
+
) {
|
|
115
|
+
if (!this.didRender) {
|
|
116
|
+
this.dispatchHighlightedTermChange();
|
|
117
|
+
}
|
|
118
|
+
const results = applySelectedAndSelect(cached.results);
|
|
119
|
+
this.dispatchChange(results);
|
|
120
|
+
this.dispatchOnLoading(false);
|
|
121
|
+
} else {
|
|
122
|
+
if (!this.didRender) {
|
|
123
|
+
this.dispatchHighlightedTermChange();
|
|
124
|
+
}
|
|
125
|
+
this.dispatchOnLoading(true);
|
|
126
|
+
this.fetchDataCloudSearch();
|
|
127
|
+
}
|
|
241
128
|
} else {
|
|
129
|
+
// No URL query: restore search bar and results from localStorage for this path
|
|
130
|
+
const baseUrlPath = getBaseUrlPath();
|
|
131
|
+
const cached = getStoredSearch(baseUrlPath);
|
|
132
|
+
if (cached?.query && !isCacheStale(cached)) {
|
|
133
|
+
this.value = cached.query;
|
|
134
|
+
const results = applySelectedAndSelect(
|
|
135
|
+
cached.results ?? []
|
|
136
|
+
);
|
|
137
|
+
this.dispatchChange(results);
|
|
138
|
+
this.dispatchHighlightedTermChange();
|
|
139
|
+
}
|
|
242
140
|
this.dispatchOnLoading(false);
|
|
243
141
|
}
|
|
244
142
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
private syncAnalytics = () => {
|
|
250
|
-
const storedResult = window.localStorage.getItem(
|
|
251
|
-
RESULT_CLICK_LOCAL_STORAGE_KEY
|
|
252
|
-
);
|
|
253
|
-
if (storedResult) {
|
|
254
|
-
const interactiveResult = buildInteractiveResult(this.engine!, {
|
|
255
|
-
options: { result: JSON.parse(storedResult) }
|
|
256
|
-
});
|
|
257
|
-
interactiveResult.cancelPendingSelect();
|
|
258
|
-
interactiveResult.select();
|
|
259
|
-
window.localStorage.removeItem(RESULT_CLICK_LOCAL_STORAGE_KEY);
|
|
260
|
-
}
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
private onResultListChange = () => {
|
|
264
|
-
if (!this.resultList) {
|
|
265
|
-
return;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const { isLoading, firstSearchExecuted, results } =
|
|
269
|
-
this.resultList.state;
|
|
270
|
-
|
|
271
|
-
if ((!firstSearchExecuted && !isLoading) || !this.value) {
|
|
272
|
-
// coveo search is firing off some unwanted payloads on startup
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
if (!isLoading) {
|
|
277
|
-
this.dispatchOnChangeEvent(results);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
if (!this.fetchingMoreResults) {
|
|
281
|
-
this.dispatchOnLoading(isLoading);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
if (!isLoading && this.fetchingMoreResults) {
|
|
285
|
-
this.fetchingMoreResults = false;
|
|
143
|
+
if (!this.didRender && this.value) {
|
|
144
|
+
this.dispatchHighlightedTermChange();
|
|
286
145
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
private setAdvancedFilters(engine: SearchEngine) {
|
|
290
|
-
const aq = Object.entries(this.coveoAdvancedQueryConfig).reduce(
|
|
291
|
-
(result, [key, value]) =>
|
|
292
|
-
`${result}(@${key}=="${normalizeCoveoAdvancedQueryValue(
|
|
293
|
-
value
|
|
294
|
-
)}")`,
|
|
295
|
-
""
|
|
296
|
-
);
|
|
297
|
-
|
|
298
|
-
const registerSearchQueryAction = loadAdvancedSearchQueryActions(
|
|
299
|
-
engine
|
|
300
|
-
).registerAdvancedSearchQueries({
|
|
301
|
-
aq: aq
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
engine.dispatch(registerSearchQueryAction);
|
|
146
|
+
this.didRender = true;
|
|
305
147
|
}
|
|
306
148
|
|
|
307
|
-
private
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
let pathname, queryParam;
|
|
321
|
-
if (sfhtml_url__c) {
|
|
322
|
-
pathname = `/docs/${sfhtml_url__c}`;
|
|
323
|
-
} else {
|
|
324
|
-
const isNestedGuide = clickUri.includes("/guide/");
|
|
325
|
-
const url = new URL(clickUri);
|
|
326
|
-
const extension =
|
|
327
|
-
isNestedGuide && !url.pathname?.endsWith(".html")
|
|
328
|
-
? ".html"
|
|
329
|
-
: "";
|
|
330
|
-
pathname = `${url.pathname}${extension}`;
|
|
331
|
-
queryParam = url.search;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
let href;
|
|
335
|
-
let isSelected = false;
|
|
336
|
-
const isReferenceUrl = clickUri.includes("/references/");
|
|
337
|
-
if (isReferenceUrl) {
|
|
338
|
-
if (queryParam) {
|
|
339
|
-
href = `${pathname}${queryParam}&q=${this.value}`;
|
|
340
|
-
} else {
|
|
341
|
-
href = `${pathname}?q=${this.value}`;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
//NOTE: This is specific to references related comparison
|
|
345
|
-
const resultHrefWithMetaQuery = `${pathname}${queryParam}`;
|
|
346
|
-
|
|
347
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
348
|
-
const metaQueryParam = urlParams.get("meta");
|
|
349
|
-
|
|
350
|
-
let currentUrlPathWithQuery = window.location.pathname;
|
|
351
|
-
if (metaQueryParam) {
|
|
352
|
-
currentUrlPathWithQuery = `${currentUrlPathWithQuery}?meta=${metaQueryParam}`;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
isSelected = resultHrefWithMetaQuery === currentUrlPathWithQuery;
|
|
356
|
-
} else {
|
|
357
|
-
href = `${pathname}?q=${this.value}`;
|
|
358
|
-
isSelected = pathname === window.location.pathname;
|
|
359
|
-
}
|
|
360
|
-
|
|
149
|
+
private normalizeDataCloudResult = (
|
|
150
|
+
item: Data360SearchResultItem,
|
|
151
|
+
index: number
|
|
152
|
+
): SidebarSearchResult => {
|
|
153
|
+
const href = item.url ?? "";
|
|
154
|
+
const resultPath = href.startsWith("http")
|
|
155
|
+
? new URL(href).pathname
|
|
156
|
+
: href;
|
|
157
|
+
const isSelected =
|
|
158
|
+
!!resultPath && resultPath === window.location.pathname;
|
|
159
|
+
const title = item.title ?? "";
|
|
160
|
+
const excerpt = item.matchedText ?? "";
|
|
161
|
+
const searchQuery = this.value.trim();
|
|
361
162
|
return {
|
|
362
163
|
title,
|
|
363
|
-
titleHighlights,
|
|
164
|
+
titleHighlights: getHighlightRanges(title, searchQuery),
|
|
364
165
|
excerpt,
|
|
365
|
-
excerptHighlights,
|
|
366
|
-
uniqueId
|
|
166
|
+
excerptHighlights: getHighlightRanges(excerpt, searchQuery),
|
|
167
|
+
uniqueId: href || `result-${index}`,
|
|
367
168
|
href,
|
|
368
169
|
selected: isSelected,
|
|
369
|
-
select: () =>
|
|
370
|
-
window.localStorage.setItem(
|
|
371
|
-
RESULT_CLICK_LOCAL_STORAGE_KEY,
|
|
372
|
-
JSON.stringify(result)
|
|
373
|
-
)
|
|
170
|
+
select: () => {}
|
|
374
171
|
};
|
|
375
172
|
};
|
|
376
173
|
|
|
377
|
-
private
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
174
|
+
private async fetchDataCloudSearch(): Promise<void> {
|
|
175
|
+
try {
|
|
176
|
+
const rawResults = await fetchSearch(this.value.trim());
|
|
177
|
+
const results: SidebarSearchResult[] = rawResults.map(
|
|
178
|
+
this.normalizeDataCloudResult
|
|
179
|
+
);
|
|
180
|
+
this.dispatchChange(results);
|
|
181
|
+
const cacheItems: Data360SearchCacheItem[] = results.map((r) => ({
|
|
182
|
+
title: r.title,
|
|
183
|
+
titleHighlights: r.titleHighlights,
|
|
184
|
+
excerpt: r.excerpt,
|
|
185
|
+
excerptHighlights: r.excerptHighlights,
|
|
186
|
+
uniqueId: r.uniqueId,
|
|
187
|
+
href: r.href
|
|
188
|
+
}));
|
|
189
|
+
setStoredSearch(getBaseUrlPath(), {
|
|
190
|
+
query: this.value.trim(),
|
|
191
|
+
results: cacheItems
|
|
192
|
+
});
|
|
193
|
+
} catch (err) {
|
|
194
|
+
console.error("Data 360 Search request failed", err);
|
|
195
|
+
this.dispatchChange([]);
|
|
196
|
+
} finally {
|
|
197
|
+
this.dispatchOnLoading(false);
|
|
198
|
+
}
|
|
385
199
|
}
|
|
386
200
|
|
|
387
|
-
private
|
|
388
|
-
this.updateSessionStorage();
|
|
201
|
+
private dispatchChange(results: SidebarSearchResult[]) {
|
|
389
202
|
this.dispatchEvent(
|
|
390
203
|
new CustomEvent("change", {
|
|
391
|
-
detail: {
|
|
392
|
-
results: results.map(this.normalizeCoveoResult),
|
|
393
|
-
value: this.value
|
|
394
|
-
}
|
|
204
|
+
detail: { results, value: this.value }
|
|
395
205
|
})
|
|
396
206
|
);
|
|
397
207
|
}
|
|
@@ -427,24 +237,18 @@ export default class SidebarSearch extends LightningElement {
|
|
|
427
237
|
}
|
|
428
238
|
|
|
429
239
|
private submitSearch = debounce((isSyncingSearchValue = false) => {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
// When `isSyncingSearchValue` is true, we are submitting in a case
|
|
435
|
-
// where the search box is already synced correctly.
|
|
436
|
-
if (!isSyncingSearchValue) {
|
|
437
|
-
this.dispatchSidebarSearchChange(this.value);
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
this.searchBox.updateText(this.value || "");
|
|
441
|
-
this.searchBox.submit();
|
|
240
|
+
UserRecentSearches.add(this.value);
|
|
241
|
+
this.updateRecentSearches();
|
|
242
|
+
if (!isSyncingSearchValue) {
|
|
243
|
+
this.dispatchSidebarSearchChange(this.value);
|
|
442
244
|
}
|
|
245
|
+
this.fetchDataCloudSearch();
|
|
443
246
|
}, SEARCH_DEBOUNCE_DELAY);
|
|
444
247
|
|
|
445
248
|
private onClickRecentSearch(e: CustomEvent) {
|
|
446
249
|
this.value = e.detail;
|
|
447
250
|
this.dispatchSidebarSearchChange(this.value);
|
|
251
|
+
this.dispatchOnLoading(true);
|
|
448
252
|
this.submitSearch(true);
|
|
449
253
|
}
|
|
450
254
|
|
|
@@ -461,7 +265,6 @@ export default class SidebarSearch extends LightningElement {
|
|
|
461
265
|
|
|
462
266
|
private onInputChange(e: CustomEvent) {
|
|
463
267
|
this.value = e.detail;
|
|
464
|
-
|
|
465
268
|
this.handleValueChange(false);
|
|
466
269
|
this.dispatchHighlightedTermChange();
|
|
467
270
|
}
|
|
@@ -477,17 +280,12 @@ export default class SidebarSearch extends LightningElement {
|
|
|
477
280
|
private handleValueChange(isSyncingSearchValue = false) {
|
|
478
281
|
if (this.value) {
|
|
479
282
|
this.dispatchOnLoading(true);
|
|
480
|
-
|
|
481
|
-
this.submitSearch(isSyncingSearchValue);
|
|
482
|
-
}
|
|
283
|
+
this.submitSearch(isSyncingSearchValue);
|
|
483
284
|
} else {
|
|
484
|
-
// coveo's reaction to an empty value triggers a return of results
|
|
485
|
-
// bootlegging our own ux here`
|
|
486
285
|
this.dispatchOnLoading(false);
|
|
487
|
-
this.
|
|
488
|
-
// Empty search values are not submitted for search, so we need to manually
|
|
489
|
-
// trigger a search change on our own here
|
|
286
|
+
this.dispatchChange([]);
|
|
490
287
|
this.dispatchSidebarSearchChange(this.value);
|
|
288
|
+
setStoredSearch(getBaseUrlPath(), { query: "", results: [] });
|
|
491
289
|
}
|
|
492
290
|
}
|
|
493
291
|
}
|