@salesforcedevs/dx-components 1.32.0-alpha.1 → 1.32.0-alpha.10

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.32.0-alpha.1",
3
+ "version": "1.32.0-alpha.10",
4
4
  "description": "DX Lightning web components",
5
5
  "license": "MIT",
6
6
  "engines": {
@@ -44,5 +44,5 @@
44
44
  "luxon": "3.4.4",
45
45
  "msw": "^2.12.4"
46
46
  },
47
- "gitHead": "9f48b66de9a139bcf6cfac4052772726f00f6d8e"
47
+ "gitHead": "f81270b339009dc62f6b32c40a3ac11911b22104"
48
48
  }
@@ -310,7 +310,7 @@ li.coveo-dynamic-facet-breadcrumb-value-list-item {
310
310
  bottom: var(--dx-result-inner-gap-y);
311
311
  left: var(--dx-result-inner-gap-x);
312
312
  right: var(--dx-result-inner-gap-x);
313
- background: var(--dx-g-gray-95);
313
+ background: var(--dx-g-cloud-blue-vibrant-95);
314
314
  border-radius: var(--dx-g-spacing-xs);
315
315
  pointer-events: none;
316
316
  z-index: 0;
@@ -326,7 +326,7 @@ li.coveo-dynamic-facet-breadcrumb-value-list-item {
326
326
  outline: none;
327
327
  }
328
328
 
329
- /* Pseudo: 8px from text on sides, more padding top/bottom inside ring (smaller inset y) */
329
+ /* Pseudo: 8px from text; inner ring container/hover (cloud blue 95), outer blue-vibrant-20 */
330
330
  .dx-result:focus-within::before {
331
331
  content: "";
332
332
  position: absolute;
@@ -334,8 +334,10 @@ li.coveo-dynamic-facet-breadcrumb-value-list-item {
334
334
  bottom: var(--dx-result-inner-gap-y);
335
335
  left: var(--dx-result-inner-gap-x);
336
336
  right: var(--dx-result-inner-gap-x);
337
- border: 2px solid var(--dx-g-blue-vibrant-50);
337
+ border: none;
338
338
  border-radius: var(--dx-g-spacing-xs);
339
+ box-shadow: 0 0 0 2px var(--dx-g-cloud-blue-vibrant-95),
340
+ 0 0 0 4px var(--dx-g-blue-vibrant-20);
339
341
  pointer-events: none;
340
342
  z-index: 2;
341
343
  }
@@ -81,6 +81,9 @@
81
81
  class="dx-result-title"
82
82
  target={result.openInNewTab}
83
83
  rel={result.rel}
84
+ data-index={result.resultIndex}
85
+ data-title={result.title}
86
+ onclick={onSearchResultClick}
84
87
  >
85
88
  {result.title}
86
89
  </a>
@@ -15,6 +15,7 @@ interface SearchResultDisplay {
15
15
  href: string;
16
16
  matchedText: string;
17
17
  uniqueId: string;
18
+ resultIndex: number;
18
19
  openInNewTab: string | undefined;
19
20
  rel: string | undefined;
20
21
  }
@@ -129,11 +130,28 @@ export default class SearchResults extends LightningElement {
129
130
  href,
130
131
  matchedText: item.matchedText ?? "",
131
132
  uniqueId: href || `result-${index}`,
133
+ resultIndex: index + 1,
132
134
  openInNewTab: isExternal ? "_blank" : undefined,
133
135
  rel: isExternal ? "noopener noreferrer" : undefined
134
136
  };
135
137
  }
136
138
 
139
+ private onSearchResultClick(e: MouseEvent) {
140
+ const anchor = e.currentTarget as HTMLAnchorElement;
141
+ const index = Number(anchor.dataset.index ?? "0");
142
+ const title = anchor.dataset.title ?? "";
143
+ const href = anchor.href ?? "";
144
+ trackGTM(anchor, "custEv_scopedSearchlinkClick", {
145
+ click_text: title,
146
+ click_url: href,
147
+ element_title: title,
148
+ element_type: "link",
149
+ content_category: "documentation",
150
+ search_term: this.query,
151
+ search_result_position: index
152
+ });
153
+ }
154
+
137
155
  private trackSearchResultsOnce(term: string, resultCount: number) {
138
156
  if (this.didTrackThisSearch) {
139
157
  return;
@@ -51,7 +51,6 @@ export default class Sidebar extends SidebarBase {
51
51
  private searchValue: string | null = null;
52
52
  private searchResults: SidebarSearchResult[] = [];
53
53
  private scrollToSelectedSearchResult: boolean = false;
54
- private selectedSearchResultIndex: number = -1;
55
54
  private requestedFetchMoreResults: boolean = false;
56
55
 
57
56
  private get areResultsEmpty(): boolean {
@@ -162,16 +161,18 @@ export default class Sidebar extends SidebarBase {
162
161
 
163
162
  private onSearchChange(e: CustomEvent) {
164
163
  this.requestedFetchMoreResults = false;
164
+ const isFirstSearchHydration = this.searchValue === null;
165
+ const hasSelectedResult = (e.detail.results || []).some(
166
+ (result: SidebarSearchResult) => !!result.selected
167
+ );
168
+ if (isFirstSearchHydration && hasSelectedResult) {
169
+ this.scrollToSelectedSearchResult = true;
170
+ }
165
171
  this.searchResults = e.detail.results;
166
172
  this.searchValue = e.detail.value;
167
173
  }
168
174
 
169
175
  private onSearchLoading(e: CustomEvent) {
170
- if (!e.detail.loading && this.scrollToSelectedSearchResult) {
171
- this.selectedSearchResultIndex = this.searchResults.findIndex(
172
- (r) => r.selected
173
- );
174
- }
175
176
  this.isSearchLoading = e.detail;
176
177
  }
177
178
 
@@ -196,23 +197,37 @@ export default class Sidebar extends SidebarBase {
196
197
  }
197
198
 
198
199
  private initializeSearchScrollPosition() {
199
- if (
200
- this.scrollToSelectedSearchResult &&
201
- this.selectedSearchResultIndex >= 0
202
- ) {
203
- const selectedResult = this.template.querySelector(
204
- `dx-sidebar-search-result:nth-child(${
205
- this.selectedSearchResultIndex + 1
206
- })`
207
- );
208
- if (!selectedResult || !selectedResult.parentNode) {
209
- return;
210
- }
211
- (selectedResult.parentNode as HTMLElement).scrollTop =
212
- selectedResult.offsetTop -
213
- (selectedResult.parentNode as HTMLElement).offsetTop;
214
- this.scrollToSelectedSearchResult = false;
200
+ if (!this.scrollToSelectedSearchResult || this.isSearchLoading) {
201
+ return;
202
+ }
203
+
204
+ const selectedSearchResultIndex = this.searchResults.findIndex(
205
+ (r) => r.selected
206
+ );
207
+ if (selectedSearchResultIndex < 0) {
208
+ return;
209
+ }
210
+
211
+ const searchContent = this.template.querySelector(
212
+ ".sidebar-content-search"
213
+ ) as HTMLElement | null;
214
+ const allResultItems = searchContent?.querySelectorAll(
215
+ "dx-sidebar-search-result"
216
+ );
217
+ const selectedResult =
218
+ (allResultItems?.[selectedSearchResultIndex] as HTMLElement) ||
219
+ null;
220
+ if (!searchContent || !selectedResult) {
221
+ return;
215
222
  }
223
+
224
+ window.requestAnimationFrame(() => {
225
+ selectedResult.scrollIntoView({
226
+ block: "start",
227
+ inline: "nearest"
228
+ });
229
+ this.scrollToSelectedSearchResult = false;
230
+ });
216
231
  }
217
232
 
218
233
  private assignValueToLabel(node: TreeNode): void {
@@ -1,5 +1,6 @@
1
1
  import { LightningElement, api } from "lwc";
2
2
  import debounce from "debounce";
3
+ import { track as trackGTM } from "dxUtils/analytics";
3
4
  import {
4
5
  type Data360SearchCacheItem,
5
6
  type Data360SearchResultItem,
@@ -85,6 +86,7 @@ export default class SidebarSearch extends LightningElement {
85
86
  private value: string = "";
86
87
  private didRender = false;
87
88
  private data360SearchInitialized: boolean = false;
89
+ private didTrackThisSearch: boolean = false;
88
90
 
89
91
  private get isDropdownOpen() {
90
92
  return (
@@ -173,11 +175,13 @@ export default class SidebarSearch extends LightningElement {
173
175
 
174
176
  private async fetchDataCloudSearch(): Promise<void> {
175
177
  try {
176
- const rawResults = await fetchSearch(this.value.trim());
178
+ const query = this.value.trim();
179
+ const rawResults = await fetchSearch(query);
177
180
  const results: SidebarSearchResult[] = rawResults.map(
178
181
  this.normalizeDataCloudResult
179
182
  );
180
183
  this.dispatchChange(results);
184
+ this.trackSearchResultsOnce(query, results.length);
181
185
  const cacheItems: Data360SearchCacheItem[] = results.map((r) => ({
182
186
  title: r.title,
183
187
  titleHighlights: r.titleHighlights,
@@ -198,6 +202,19 @@ export default class SidebarSearch extends LightningElement {
198
202
  }
199
203
  }
200
204
 
205
+ private trackSearchResultsOnce(term: string, resultCount: number): void {
206
+ if (this.didTrackThisSearch || !term) {
207
+ return;
208
+ }
209
+ this.didTrackThisSearch = true;
210
+ trackGTM(this.template.host, "custEv_scopedSearch", {
211
+ search_term: term,
212
+ search_category: "",
213
+ search_type: "site search",
214
+ search_result_count: resultCount
215
+ });
216
+ }
217
+
201
218
  private dispatchChange(results: SidebarSearchResult[]) {
202
219
  this.dispatchEvent(
203
220
  new CustomEvent("change", {
@@ -279,6 +296,7 @@ export default class SidebarSearch extends LightningElement {
279
296
 
280
297
  private handleValueChange(isSyncingSearchValue = false) {
281
298
  if (this.value) {
299
+ this.didTrackThisSearch = false;
282
300
  this.dispatchOnLoading(true);
283
301
  this.submitSearch(isSyncingSearchValue);
284
302
  } else {
@@ -10,8 +10,7 @@ a {
10
10
 
11
11
  /* Match "Results" heading alignment. Less item padding so total height unchanged when box has more inner padding. */
12
12
  .sidebar-item {
13
- padding: var(--dx-g-spacing-xs) var(--dx-g-spacing-lg)
14
- var(--dx-g-spacing-xs) var(--dx-c-sidebar-left-padding);
13
+ padding: 0 var(--dx-g-spacing-lg) 0 var(--dx-c-sidebar-left-padding);
15
14
  background: transparent;
16
15
  transition: background-color var(--dx-g-transition-hue-1x, 0.1s ease);
17
16
  overflow: visible;
@@ -26,32 +25,47 @@ a {
26
25
  transition: background-color var(--dx-g-transition-hue-1x, 0.1s ease);
27
26
  }
28
27
 
29
- /* Hover: light grey background in box shape only */
28
+ /* Hover: container/hover cloud blue 95 */
30
29
  .sidebar-item:not(.sidebar-item-selected):hover .search-result {
31
- background: var(--dx-g-gray-95);
30
+ background: var(--dx-g-cloud-blue-vibrant-95);
32
31
  }
33
32
 
34
- /* Selected: light blue background in box shape only */
33
+ /* Selected: full-width cloud blue bar + 4px blue-vibrant-40 left accent; text matches unselected */
35
34
  .sidebar-item-selected {
36
- box-shadow: none;
37
- color: var(--dx-g-blue-vibrant-40) !important;
35
+ background: var(--dx-g-cloud-blue-vibrant-95);
36
+ border-radius: 0;
37
+ box-shadow: inset var(--dx-g-spacing-xs) 0 0 0 var(--dx-g-blue-vibrant-40);
38
+
39
+ /* Override commonTreeItem selected text color so .title / .description tokens apply */
40
+ color: unset !important;
38
41
  }
39
42
 
40
43
  .sidebar-item-selected .search-result {
41
- background: var(--dx-g-cloud-blue-vibrant-95);
44
+ position: relative;
45
+ z-index: 1;
46
+ background: transparent;
42
47
  color: inherit;
43
48
  }
44
49
 
45
- /* Focus: only one box remove anchor outline (from commonTreeItem), draw single blue outline on .search-result only */
50
+ /* Focus: same ring for selected and unselected (inner .search-result, not full-width bar) */
46
51
  .sidebar-item:focus-visible {
47
52
  outline: none !important;
48
53
  }
49
54
 
55
+ /* Override commonTreeItem selected:focus padding/outline so focus matches unselected */
56
+ .sidebar-item-selected:focus-visible {
57
+ padding-top: 0;
58
+ padding-bottom: 0;
59
+ outline: none !important;
60
+ }
61
+
50
62
  .sidebar-item:focus-visible .search-result {
51
- outline: 2px solid var(--dx-g-blue-vibrant-50);
52
- outline-offset: 0;
63
+ outline: none;
53
64
  border-radius: var(--dx-g-spacing-xs);
54
65
  border: none;
66
+
67
+ /* White inner ring reads clearly on selected (cloud 95) and hover backgrounds */
68
+ box-shadow: 0 0 0 2px white, 0 0 0 4px var(--dx-g-blue-vibrant-60);
55
69
  }
56
70
 
57
71
  .search-text {
@@ -63,6 +77,7 @@ a {
63
77
  }
64
78
 
65
79
  .description {
80
+ line-clamp: 3;
66
81
  -webkit-line-clamp: 3;
67
82
  overflow-wrap: break-word;
68
83
 
@@ -74,6 +89,9 @@ a {
74
89
 
75
90
  .title {
76
91
  color: var(--dx-g-text-heading-color);
92
+ line-height: 18px;
93
+ display: block;
94
+ margin-bottom: 4px;
77
95
  }
78
96
 
79
97
  /* Match full-doc search highlight: light yellow from dx-css-variables */
@@ -81,7 +99,3 @@ a {
81
99
  background-color: var(--dx-g-yellow-vibrant-90);
82
100
  font-weight: 700;
83
101
  }
84
-
85
- a > *:not(:last-child) {
86
- margin-bottom: var(--dx-g-spacing-xs);
87
- }
@@ -1,5 +1,6 @@
1
1
  import { LightningElement, api } from "lwc";
2
2
  import cx from "classnames";
3
+ import { track } from "dxUtils/analytics";
3
4
  import { HighlightedSections } from "typings/custom";
4
5
 
5
6
  const toChunks = (value: string, highlights: HighlightedSections) => {
@@ -72,6 +73,13 @@ export default class SidebarSearchResult extends LightningElement {
72
73
 
73
74
  private onClick(e: PointerEvent) {
74
75
  e.preventDefault();
76
+ track(e.currentTarget!, "custEv_scopedSearchItemSelected", {
77
+ click_text: this.header,
78
+ click_url: this.href,
79
+ element_title: this.header,
80
+ element_type: "link",
81
+ content_category: "documentation"
82
+ });
75
83
  this.select();
76
84
  window.location.href = this.href;
77
85
  }