@salesforcedevs/dx-components 1.32.0-alpha.9 → 1.32.0

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.
@@ -2,63 +2,34 @@
2
2
  @import "dxHelpers/text";
3
3
  @import "dxHelpers/commonTreeItem";
4
4
 
5
- /* List item states per spec: Default, Hover (#F7F7F7), Selected (#E6F2FF), Focus (blue outline), Focus+Selected */
6
5
  a {
7
6
  display: flex;
8
7
  flex-direction: column;
9
8
  }
10
9
 
11
- /* Match "Results" heading alignment. Less item padding so total height unchanged when box has more inner padding. */
12
10
  .sidebar-item {
13
- padding: 0 var(--dx-g-spacing-lg) 0 var(--dx-c-sidebar-left-padding);
14
- background: transparent;
15
- transition: background-color var(--dx-g-transition-hue-1x, 0.1s ease);
16
- overflow: visible;
11
+ padding: var(--dx-g-spacing-smd) var(--dx-g-spacing-lg)
12
+ var(--dx-g-spacing-smd) var(--dx-g-global-header-padding-horizontal);
17
13
  }
18
14
 
19
- /* Inner block: more vertical padding (12px) so box has more space top/bottom; 8px horizontal. margin-left so text aligns with Results. */
20
- .search-result {
21
- margin-left: calc(-1 * var(--dx-g-spacing-sm));
22
- padding: var(--dx-g-spacing-smd) var(--dx-g-spacing-sm);
23
- border: none;
24
- border-radius: var(--dx-g-spacing-xs);
25
- transition: background-color var(--dx-g-transition-hue-1x, 0.1s ease);
15
+ .sidebar-item:not(.sidebar-item-selected):focus-visible {
16
+ padding: var(--dx-g-spacing-smd) 0;
17
+ margin: 0 var(--dx-g-spacing-lg) 0
18
+ var(--dx-g-global-header-padding-horizontal);
19
+ outline: var(--dx-g-spacing-2xs) solid var(--dx-g-blue-vibrant-40);
20
+ border-radius: var(--dx-g-spacing-2xs);
26
21
  }
27
22
 
28
- /* Hover: container/hover — cloud blue 95 */
29
- .sidebar-item:not(.sidebar-item-selected):hover .search-result {
30
- background: var(--dx-g-cloud-blue-vibrant-95);
31
- }
32
-
33
- /* Selected: full-width cloud blue bar + dark blue left accent (focus ring stays on inner .search-result) */
34
- .sidebar-item-selected {
35
- margin-left: calc(-1 * var(--dx-c-sidebar-left-padding));
36
- margin-right: calc(-1 * var(--dx-g-spacing-lg));
37
- background: var(--dx-g-cloud-blue-vibrant-95);
38
- border-radius: 0;
39
- box-shadow: inset var(--dx-g-spacing-xs) 0 0 0 var(--dx-g-blue-vibrant-20);
40
- color: var(--dx-g-blue-vibrant-40) !important;
41
- }
42
-
43
- .sidebar-item-selected .search-result {
44
- position: relative;
45
- z-index: 1;
46
- background: transparent;
47
- color: inherit;
48
- }
49
-
50
- /* Focus: rings on inner .search-result only (smaller than full-width selected bar) */
51
- .sidebar-item:focus-visible {
52
- outline: none !important;
53
- }
54
-
55
- .sidebar-item:focus-visible .search-result {
23
+ .sidebar-item-selected:focus-visible {
24
+ padding: 0 var(--dx-g-spacing-lg) 0
25
+ var(--dx-g-global-header-padding-horizontal);
56
26
  outline: none;
57
- border-radius: var(--dx-g-spacing-xs);
58
- border: none;
59
27
 
60
- /* White inner ring reads clearly on selected (cloud 95) and hover backgrounds */
61
- box-shadow: 0 0 0 2px white, 0 0 0 4px var(--dx-g-blue-vibrant-60);
28
+ .search-result {
29
+ outline: var(--dx-g-spacing-2xs) solid var(--dx-g-blue-vibrant-40);
30
+ border-radius: var(--dx-g-spacing-2xs);
31
+ padding: var(--dx-g-spacing-smd) 0;
32
+ }
62
33
  }
63
34
 
64
35
  .search-text {
@@ -70,7 +41,6 @@ a {
70
41
  }
71
42
 
72
43
  .description {
73
- line-clamp: 3;
74
44
  -webkit-line-clamp: 3;
75
45
  overflow-wrap: break-word;
76
46
 
@@ -82,13 +52,16 @@ a {
82
52
 
83
53
  .title {
84
54
  color: var(--dx-g-text-heading-color);
85
- line-height: 18px;
86
- display: block;
87
- margin-bottom: 4px;
88
55
  }
89
56
 
90
- /* Match full-doc search highlight: light yellow from dx-css-variables */
57
+ .title:hover {
58
+ color: var(--dx-g-blue-vibrant-50);
59
+ }
60
+
91
61
  .bold {
92
- background-color: var(--dx-g-yellow-vibrant-90);
93
62
  font-weight: 700;
94
63
  }
64
+
65
+ a > *:not(:last-child) {
66
+ margin-bottom: var(--dx-g-spacing-xs);
67
+ }
@@ -1,9 +1,8 @@
1
1
  import { LightningElement, api } from "lwc";
2
2
  import cx from "classnames";
3
- import { track } from "dxUtils/analytics";
4
- import { HighlightedSections } from "typings/custom";
3
+ import { CoveoHighlights } from "typings/custom";
5
4
 
6
- const toChunks = (value: string, highlights: HighlightedSections) => {
5
+ const toChunks = (value: string, highlights: CoveoHighlights) => {
7
6
  if (!highlights || highlights.length < 1) {
8
7
  return [
9
8
  {
@@ -52,11 +51,11 @@ const toChunks = (value: string, highlights: HighlightedSections) => {
52
51
 
53
52
  export default class SidebarSearchResult extends LightningElement {
54
53
  @api description!: string;
55
- @api descriptionHighlights!: HighlightedSections;
54
+ @api descriptionHighlights!: CoveoHighlights;
56
55
  @api href!: string;
57
56
  @api selected!: boolean;
58
57
  @api header!: string;
59
- @api titleHighlights!: HighlightedSections;
58
+ @api titleHighlights!: CoveoHighlights;
60
59
  @api select!: Function;
61
60
 
62
61
  private get titleChunks() {
@@ -73,13 +72,6 @@ export default class SidebarSearchResult extends LightningElement {
73
72
 
74
73
  private onClick(e: PointerEvent) {
75
74
  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
- });
83
75
  this.select();
84
76
  window.location.href = this.href;
85
77
  }
@@ -64,7 +64,8 @@ export class TreeHandler {
64
64
  isExpanded: node.isExpanded || false,
65
65
  isChildrenLoading: node.isChildrenLoading || false,
66
66
  link: node.link,
67
- method: node.method
67
+ method: node.method,
68
+ showForwardArrow: node.showForwardArrow
68
69
  };
69
70
 
70
71
  const mapItem = { node: convertedNode, parent };
@@ -58,6 +58,10 @@
58
58
  margin-left: var(--dx-g-spacing-xs);
59
59
  }
60
60
 
61
+ .tile-forward-arrow {
62
+ margin-left: auto;
63
+ }
64
+
61
65
  .whitespace-nowrap {
62
66
  white-space: nowrap;
63
67
  }
@@ -37,6 +37,16 @@
37
37
  value={methodText}
38
38
  color={metadataBadgeColor}
39
39
  ></dx-metadata-badge>
40
+ <div
41
+ class="flex tile-icon tile-forward-arrow"
42
+ if:true={showForwardArrow}
43
+ >
44
+ <dx-icon
45
+ sprite="utility"
46
+ symbol="forward"
47
+ size={iconSize}
48
+ ></dx-icon>
49
+ </div>
40
50
  </div>
41
51
  </a>
42
52
  </template>
@@ -52,6 +52,10 @@ export default class TreeTile extends LightningElement {
52
52
  return this.treeNode.method && !this.hasChildren;
53
53
  }
54
54
 
55
+ private get showForwardArrow() {
56
+ return this.treeNode.showForwardArrow;
57
+ }
58
+
55
59
  private get methodText() {
56
60
  if (this.treeNode.method && this.treeNode.method in methodTextMap) {
57
61
  return methodTextMap[this.treeNode.method];
@@ -43,3 +43,35 @@ export function pollUntil(
43
43
  }
44
44
  });
45
45
  }
46
+
47
+ /**
48
+ * Returns the same settled value or rejection as `promise`, or rejects after `forMaximumMs`
49
+ * if it does not settle in time. When `forMaximumMs` is omitted or non-positive, returns
50
+ * `promise` unchanged. Clears the timeout when `promise` settles first.
51
+ */
52
+ export function waitUntilResolved<T>(
53
+ promise: Promise<T>,
54
+ forMaximumMs?: number,
55
+ timeoutMessage = "Timed out waiting for promise to settle."
56
+ ): Promise<T> {
57
+ if (forMaximumMs == null || forMaximumMs <= 0) {
58
+ return promise;
59
+ }
60
+
61
+ return new Promise<T>((resolve, reject) => {
62
+ const timeoutId = setTimeout(() => {
63
+ reject(new Error(timeoutMessage));
64
+ }, forMaximumMs);
65
+
66
+ promise.then(
67
+ (value) => {
68
+ clearTimeout(timeoutId);
69
+ resolve(value);
70
+ },
71
+ (reason: unknown) => {
72
+ clearTimeout(timeoutId);
73
+ reject(reason);
74
+ }
75
+ );
76
+ });
77
+ }
@@ -1,168 +0,0 @@
1
- /**
2
- * Data 360 search API – shared endpoints and helpers for sidebar search,
3
- * search results page, and has-results check.
4
- */
5
-
6
- const STORAGE_KEY_PREFIX = "data360-search:";
7
-
8
- /** Data 360 Search API result item (title, url, matchedText) */
9
- export interface Data360SearchResultItem {
10
- title?: string;
11
- url?: string;
12
- matchedText?: string;
13
- }
14
-
15
- /** Normalized result shape stored in cache (no selected/select; those are applied on restore). */
16
- export interface Data360SearchCacheItem {
17
- title: string;
18
- titleHighlights: Array<{ length: number; offset: number }>;
19
- excerpt: string;
20
- excerptHighlights: Array<{ length: number; offset: number }>;
21
- uniqueId: string;
22
- href: string;
23
- }
24
-
25
- export interface Data360SearchCache {
26
- query: string;
27
- results: Data360SearchCacheItem[];
28
- /** Timestamp when stored (Date.now()); missing on older entries (treated as stale). */
29
- storedAt?: number;
30
- }
31
-
32
- const CACHE_MAX_AGE_MS = 2 * 60 * 60 * 1000; // 2 hours
33
-
34
- export function isCacheStale(cache: Data360SearchCache | null): boolean {
35
- if (!cache?.storedAt) {
36
- return true;
37
- }
38
- return Date.now() - cache.storedAt > CACHE_MAX_AGE_MS;
39
- }
40
-
41
- export function getStoredSearch(
42
- baseUrlPath: string
43
- ): Data360SearchCache | null {
44
- try {
45
- const raw = localStorage.getItem(`${STORAGE_KEY_PREFIX}${baseUrlPath}`);
46
- if (!raw) {
47
- return null;
48
- }
49
- const parsed = JSON.parse(raw) as Data360SearchCache;
50
- if (
51
- !parsed ||
52
- typeof parsed.query !== "string" ||
53
- !Array.isArray(parsed.results)
54
- ) {
55
- return null;
56
- }
57
- return parsed;
58
- } catch {
59
- return null;
60
- }
61
- }
62
-
63
- export function setStoredSearch(
64
- baseUrlPath: string,
65
- data: Data360SearchCache
66
- ): void {
67
- try {
68
- const toStore: Data360SearchCache = {
69
- ...data,
70
- storedAt: Date.now()
71
- };
72
- localStorage.setItem(
73
- `${STORAGE_KEY_PREFIX}${baseUrlPath}`,
74
- JSON.stringify(toStore)
75
- );
76
- } catch {
77
- /* ignore quota or disabled localStorage */
78
- }
79
- }
80
-
81
- export const DATA_360_SEARCH_PATH = "/data-360-search/search";
82
- export const DATA_360_HAS_RESULTS_PATH = "/data-360-search/has-results";
83
- export const DATA_360_SEARCH_ORIGIN = "https://developer.salesforce.com";
84
-
85
- export function getBaseUrlPath(): string {
86
- const url = DATA_360_SEARCH_ORIGIN + window.location.pathname;
87
- const lastSlash = url.lastIndexOf("/");
88
- return lastSlash > 0 ? url.substring(0, lastSlash) : url;
89
- }
90
-
91
- /**
92
- * Read search query from URL: ?q=, ?keywords= (migrated to q), or #q=.
93
- */
94
- export function getQueryFromUrl(): string {
95
- const params = new URLSearchParams(window.location.search);
96
- const keywords = params.get("keywords");
97
- if (keywords) {
98
- const url = new URL(window.location.href);
99
- url.searchParams.delete("keywords");
100
- url.searchParams.set("q", keywords);
101
- window.history.replaceState(null, "", url.href);
102
- return keywords;
103
- }
104
- const qFromSearch = params.get("q");
105
- if (qFromSearch) {
106
- return qFromSearch;
107
- }
108
- const hash = window.location.hash.slice(1);
109
- if (hash) {
110
- try {
111
- const qFromHash = new URLSearchParams(hash).get("q");
112
- if (qFromHash) {
113
- return qFromHash;
114
- }
115
- } catch {
116
- /* ignore */
117
- }
118
- }
119
- return "";
120
- }
121
-
122
- /**
123
- * Quick check if the current page has searchable results.
124
- * Only when this returns true should the Data 360 sidebar be shown.
125
- */
126
- export async function fetchHasResults(): Promise<boolean> {
127
- try {
128
- const res = await fetch(DATA_360_HAS_RESULTS_PATH, {
129
- method: "POST",
130
- headers: { "Content-Type": "application/json" },
131
- body: JSON.stringify({ baseUrlPath: getBaseUrlPath() })
132
- });
133
- if (!res.ok) {
134
- return false;
135
- }
136
- const data = await res.json();
137
- return data.hasResults === true;
138
- } catch {
139
- return false;
140
- }
141
- }
142
-
143
- /**
144
- * Run a Data 360 search. Returns the raw result items; callers normalize for UI.
145
- */
146
- export async function fetchSearch(
147
- searchQuery: string
148
- ): Promise<Data360SearchResultItem[]> {
149
- const query = searchQuery.trim();
150
- if (!query) {
151
- return [];
152
- }
153
- const res = await fetch(DATA_360_SEARCH_PATH, {
154
- method: "POST",
155
- headers: { "Content-Type": "application/json" },
156
- body: JSON.stringify({
157
- searchQuery: query,
158
- baseUrlPath: getBaseUrlPath()
159
- })
160
- });
161
- if (!res.ok) {
162
- throw new Error(`Search API error: ${res.status}`);
163
- }
164
- const data = await res.json();
165
- return Array.isArray(data)
166
- ? data
167
- : data?.results ?? data?.data?.results ?? [];
168
- }