@salesforcedevs/dx-components 0.10.1-alpha.9 → 0.10.1-alpha.93

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.
Files changed (29) hide show
  1. package/lwc.config.json +3 -1
  2. package/package.json +1 -1
  3. package/src/assets/svg/sidebar-loading.svg +34 -0
  4. package/src/modules/dx/cardGrid/__tests__/cardMockProps.ts +78 -148
  5. package/src/modules/dx/codeBlock/codeBlock.stories.ts +2 -69
  6. package/src/modules/dx/codeBlock/codeBlock.ts +5 -1
  7. package/src/modules/dx/codeBlock/darkTheme.css +0 -1
  8. package/src/modules/dx/codeBlock/lightTheme.css +0 -1
  9. package/src/modules/dx/footer/links.ts +3 -5
  10. package/src/modules/dx/searchResults/coveo.css +314 -111
  11. package/src/modules/dx/sidebar/sidebar.css +38 -17
  12. package/src/modules/dx/sidebar/sidebar.html +61 -58
  13. package/src/modules/dx/sidebar/sidebar.stories.ts +69 -49
  14. package/src/modules/dx/sidebar/sidebar.ts +89 -11
  15. package/src/modules/dx/sidebarSearch/sidebarSearch.ts +4310 -86
  16. package/src/modules/dx/sidebarSearch/utils.ts +2 -0
  17. package/src/modules/dx/sidebarSearchResult/sidebarSearchResult.css +11 -1
  18. package/src/modules/dx/sidebarSearchResult/sidebarSearchResult.html +1 -1
  19. package/src/modules/dx/sidebarSearchResult/sidebarSearchResult.ts +8 -2
  20. package/src/modules/dx/spinner/__tests__/spinner.test.ts +37 -0
  21. package/src/modules/dx/spinner/spinner.css +750 -0
  22. package/src/modules/dx/spinner/spinner.html +6 -0
  23. package/src/modules/dx/spinner/spinner.stories.ts +69 -0
  24. package/src/modules/dx/spinner/spinner.ts +20 -0
  25. package/src/modules/helpers/table/table.css +23 -7
  26. package/src/modules/utils/coveo/coveo.ts +3 -0
  27. package/src/modules/utils/highlight/highlight.ts +31 -0
  28. package/src/modules/utils/recentSearches/recentSearches.ts +8 -4
  29. package/src/modules/utils/regexps/regexps.ts +5 -0
@@ -13,6 +13,13 @@
13
13
  --dx-c-sidebar-vertical-padding: var(--dx-g-spacing-md);
14
14
  }
15
15
 
16
+ dx-empty-state {
17
+ --dx-c-empty-state-background: var(--sds-g-gray-2);
18
+ --dx-c-empty-state-image-width: 400px;
19
+
20
+ display: none;
21
+ }
22
+
16
23
  dx-tree:not(:last-child) {
17
24
  margin-bottom: var(--dx-g-spacing-sm);
18
25
  }
@@ -57,14 +64,6 @@ dx-tree:not(:last-child) {
57
64
  transition: var(--dx-g-transition-transform-2x);
58
65
  }
59
66
 
60
- .container.collapsed {
61
- width: var(--dx-g-spacing-xl);
62
- }
63
-
64
- .collapsed .header-toggle-button {
65
- transform: rotate(180deg);
66
- }
67
-
68
67
  .container {
69
68
  background-color: var(--dx-g-gray-95);
70
69
  display: flex;
@@ -74,12 +73,12 @@ dx-tree:not(:last-child) {
74
73
  }
75
74
 
76
75
  .container:not(.container-mobile) {
77
- --border-color: var(--dx-g-gray-95);
76
+ --border-color: transparent;
78
77
  --left-spacing: calc(
79
78
  var(--dx-c-sidebar-left-padding) - var(--dx-g-spacing-md)
80
79
  );
81
80
 
82
- border-right: 2px solid var(--border-color);
81
+ box-shadow: 2px 0 0 0 var(--border-color);
83
82
  min-height: var(--dx-c-sidebar-min-height);
84
83
  padding: var(--dx-c-sidebar-vertical-padding) 0
85
84
  var(--dx-c-sidebar-vertical-padding) var(--left-spacing);
@@ -94,10 +93,6 @@ dx-tree:not(:last-child) {
94
93
  --border-color: transparent;
95
94
  }
96
95
 
97
- .container:hover .header-toggle-button {
98
- --dx-c-button-opacity: 1;
99
- }
100
-
101
96
  .container.container-border-active {
102
97
  --border-color: var(--dx-c-sidebar-button-color);
103
98
  }
@@ -152,9 +147,35 @@ dx-tree:not(:last-child) {
152
147
  white-space: nowrap;
153
148
  }
154
149
 
155
- dx-empty-state {
156
- --dx-c-empty-state-background: var(--sds-g-gray-2);
157
- --dx-c-empty-state-image-width: 400px;
150
+ .loading-skeleton {
151
+ margin-top: var(--dx-g-spacing-md);
152
+ }
158
153
 
154
+ .search-empty.container dx-empty-state {
159
155
  display: block;
160
156
  }
157
+
158
+ .user-searching.container .sidebar-content-tree {
159
+ display: none;
160
+ }
161
+
162
+ .show-tree.container .sidebar-content-search {
163
+ display: none;
164
+ }
165
+
166
+ .container.collapsed {
167
+ width: var(--dx-g-spacing-xl);
168
+ }
169
+
170
+ .container.collapsed .header-toggle-button {
171
+ transform: rotate(180deg);
172
+ }
173
+
174
+ .container.collapsed .sidebar-header,
175
+ .container.collapsed .sidebar-content {
176
+ display: none;
177
+ }
178
+
179
+ .container:hover .header-toggle-button {
180
+ --dx-c-button-opacity: 1;
181
+ }
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class={containerClass}>
2
+ <div class={containerClass} part="container">
3
3
  <div
4
4
  if:true={mobile}
5
5
  class="header padding-horizontal"
@@ -27,64 +27,67 @@
27
27
  onmouseenter={onToggleMouseEnter}
28
28
  onmouseleave={onToggleMouseLeave}
29
29
  ></dx-button>
30
-
31
- <template if:true={expanded}>
32
- <div class="padding-horizontal">
33
- <div class="header" if:false={mobile}>
34
- <h2 class="dx-text-heading-4 header-title">{header}</h2>
35
- <slot name="header"></slot>
36
- </div>
37
- <div class="search">
38
- <dx-sidebar-search
39
- onchange={onSearchChange}
40
- onloading={onSearchLoading}
41
- coveo-organization-id={coveoOrganizationId}
42
- coveo-public-access-token={coveoPublicAccessToken}
43
- coveo-search-hub={coveoSearchHub}
44
- coveo-object-type={coveoObjectType}
45
- coveo-language={coveoLanguage}
46
- coveo-release={coveoRelease}
47
- coveo-deliverable={coveoDeliverable}
48
- ></dx-sidebar-search>
49
- </div>
50
- <span if:true={isSearchLoading}>Loading...</span>
51
- <h2
52
- class="results-heading dx-text-heading-6"
53
- if:true={showResultsHeading}
54
- >
55
- Results
56
- </h2>
30
+ <div class="sidebar-header padding-horizontal">
31
+ <div class="header" if:false={mobile}>
32
+ <h2 class="dx-text-heading-4 header-title">{header}</h2>
33
+ <slot name="header"></slot>
57
34
  </div>
58
- <div class="sidebar-content">
59
- <dx-tree
60
- if:false={isUserSearching}
61
- for:each={trees}
62
- for:item="tree"
63
- key={tree.key}
64
- tree-root={tree.tree}
65
- onselect={onSelect}
66
- value={value}
67
- ></dx-tree>
68
- <template
69
- if:true={isUserSearching}
70
- for:each={searchResults}
71
- for:item="result"
72
- >
73
- <dx-sidebar-search-result
74
- title={result.title}
75
- title-highlights={result.titleHighlights}
76
- description={result.excerpt}
77
- description-highlights={result.excerptHighlights}
78
- href={result.href}
79
- key={result.uniqueId}
80
- ></dx-sidebar-search-result>
81
- </template>
82
- <dx-empty-state
83
- class="padding-horizontal"
84
- title="No Results"
85
- if:true={isSearchEmpty}
86
- ></dx-empty-state>
35
+ <div class="search">
36
+ <dx-sidebar-search
37
+ onchange={onSearchChange}
38
+ onloading={onSearchLoading}
39
+ coveo-organization-id={coveoOrganizationId}
40
+ coveo-public-access-token={coveoPublicAccessToken}
41
+ coveo-search-hub={coveoSearchHub}
42
+ coveo-advanced-query-config={coveoAdvancedQueryConfig}
43
+ ></dx-sidebar-search>
87
44
  </div>
88
- </template>
45
+ <h2
46
+ class="results-heading dx-text-heading-6"
47
+ if:true={showResultsHeading}
48
+ >
49
+ Results
50
+ </h2>
51
+ </div>
52
+ <div
53
+ class="sidebar-content sidebar-content-search"
54
+ onscroll={onSidebarSearchContentScroll}
55
+ >
56
+ <img
57
+ if:true={isSearchLoading}
58
+ class="loading-skeleton padding-horizontal"
59
+ src="/assets/svg/sidebar-loading.svg"
60
+ alt="loading"
61
+ />
62
+ <template
63
+ if:true={showSearchResults}
64
+ for:each={searchResults}
65
+ for:item="result"
66
+ >
67
+ <dx-sidebar-search-result
68
+ description-highlights={result.excerptHighlights}
69
+ description={result.excerpt}
70
+ href={result.href}
71
+ key={result.uniqueId}
72
+ selected={result.selected}
73
+ title-highlights={result.titleHighlights}
74
+ title={result.title}
75
+ ></dx-sidebar-search-result>
76
+ </template>
77
+ <dx-empty-state
78
+ class="padding-horizontal"
79
+ title="No Results"
80
+ ></dx-empty-state>
81
+ </div>
82
+ <div class="sidebar-content sidebar-content-tree">
83
+ <dx-tree
84
+ for:each={trees}
85
+ for:item="tree"
86
+ key={tree.key}
87
+ tree-root={tree.tree}
88
+ onselect={onSelect}
89
+ value={value}
90
+ ></dx-tree>
91
+ </div>
89
92
  </div>
90
93
  </template>
@@ -1,14 +1,26 @@
1
1
  import { html } from "lit-html";
2
2
  import { SAMPLE_TREES, SAMPLE_TREES_WITH_METHODS } from "./__tests__/mockProps";
3
3
 
4
- export const coveoConfig = {
4
+ const coveoXMLDocsConfig = {
5
5
  coveoOrganizationId: "coveosalesforcetestakshatha",
6
6
  coveoPublicAccessToken: "xxfeb39525-e402-4e41-a870-7132441f7875",
7
7
  coveoSearchHub: "Developer_Docs_SS",
8
- coveoObjectType: "HTDeveloperDocumentsC",
9
- coveoLanguage: "en_US",
10
- coveoRelease: "232.0",
11
- coveoDeliverable: "apexcode"
8
+ coveoAdvancedQueryConfig: JSON.stringify({
9
+ objecttype: "HTDeveloperDocumentsC",
10
+ sflanguage: "en_US",
11
+ sfrelease__c: "232.0",
12
+ sfdelivarable__c: "apexcode"
13
+ })
14
+ };
15
+
16
+ const coveoSFDocsConfig = {
17
+ coveoOrganizationId: "coveosalesforcetestakshatha",
18
+ coveoPublicAccessToken: "xxfeb39525-e402-4e41-a870-7132441f7875",
19
+ coveoSearchHub: "Developer_MdDocs_SS",
20
+ coveoAdvancedQueryConfig: JSON.stringify({
21
+ source: "Sitemap - Developer Docs",
22
+ path_segment_3: "pardot"
23
+ })
12
24
  };
13
25
 
14
26
  export default {
@@ -42,25 +54,48 @@ const styles = (addStyles: string = "") => html`
42
54
  ${addStyles}
43
55
  }
44
56
 
57
+ dx-sidebar::part(container) {
58
+ height: 100vh;
59
+ }
60
+
45
61
  body.sb-main-padded.sb-show-main {
46
62
  padding: 0;
47
63
  }
64
+
65
+ #root-inner {
66
+ display: flex;
67
+ height: 100%;
68
+ }
69
+
70
+ doc-content {
71
+ padding: 20px;
72
+ }
48
73
  </style>
49
74
  `;
50
75
 
51
- export const Base = (args: any) => html`
52
- ${styles("--dx-c-sidebar-min-height: 100vh;")}
76
+ export const XMLDocs = (args: any) => html`
77
+ ${styles("--dx-c-sidebar-min-height: 100%;")}
78
+
79
+ <dx-sidebar
80
+ header="${args.header}"
81
+ trees="${JSON.stringify(args.trees)}"
82
+ coveo-organization-id="${coveoXMLDocsConfig.coveoOrganizationId}"
83
+ coveo-public-access-token="${coveoXMLDocsConfig.coveoPublicAccessToken}"
84
+ coveo-search-hub="${coveoXMLDocsConfig.coveoSearchHub}"
85
+ coveo-advanced-query-config="${coveoXMLDocsConfig.coveoAdvancedQueryConfig}"
86
+ ></dx-sidebar>
87
+ `;
88
+
89
+ export const SFDocs = (args: any) => html`
90
+ ${styles("--dx-c-sidebar-min-height: 100%;")}
53
91
 
54
92
  <dx-sidebar
55
93
  header="${args.header}"
56
94
  trees="${JSON.stringify(args.trees)}"
57
- coveo-organization-id="${coveoConfig.coveoOrganizationId}"
58
- coveo-public-access-token="${coveoConfig.coveoPublicAccessToken}"
59
- coveo-search-hub="${coveoConfig.coveoSearchHub}"
60
- coveo-object-type="${coveoConfig.coveoObjectType}"
61
- coveo-language="${coveoConfig.coveoLanguage}"
62
- coveo-release="${coveoConfig.coveoRelease}"
63
- coveo-deliverable="${coveoConfig.coveoDeliverable}"
95
+ coveo-organization-id="${coveoSFDocsConfig.coveoOrganizationId}"
96
+ coveo-public-access-token="${coveoSFDocsConfig.coveoPublicAccessToken}"
97
+ coveo-search-hub="${coveoSFDocsConfig.coveoSearchHub}"
98
+ coveo-advanced-query-config="${coveoSFDocsConfig.coveoAdvancedQueryConfig}"
64
99
  ></dx-sidebar>
65
100
  `;
66
101
 
@@ -74,13 +109,10 @@ export const WidthAndMinHeightVariables = (args: any) => html`
74
109
  <dx-sidebar
75
110
  header="${args.header}"
76
111
  trees="${JSON.stringify(args.trees)}"
77
- coveo-organization-id="${coveoConfig.coveoOrganizationId}"
78
- coveo-public-access-token="${coveoConfig.coveoPublicAccessToken}"
79
- coveo-search-hub="${coveoConfig.coveoSearchHub}"
80
- coveo-object-type="${coveoConfig.coveoObjectType}"
81
- coveo-language="${coveoConfig.coveoLanguage}"
82
- coveo-release="${coveoConfig.coveoRelease}"
83
- coveo-deliverable="${coveoConfig.coveoDeliverable}"
112
+ coveo-organization-id="${coveoXMLDocsConfig.coveoOrganizationId}"
113
+ coveo-public-access-token="${coveoXMLDocsConfig.coveoPublicAccessToken}"
114
+ coveo-search-hub="${coveoXMLDocsConfig.coveoSearchHub}"
115
+ coveo-advanced-query-config="${coveoXMLDocsConfig.coveoAdvancedQueryConfig}"
84
116
  ></dx-sidebar>
85
117
  `;
86
118
 
@@ -103,13 +135,10 @@ export const WidthDrivenByContainer = (args: any) => html`
103
135
  header="${args.header}"
104
136
  trees="${JSON.stringify(args.trees)}"
105
137
  value="${args.value}"
106
- coveo-organization-id="${coveoConfig.coveoOrganizationId}"
107
- coveo-public-access-token="${coveoConfig.coveoPublicAccessToken}"
108
- coveo-search-hub="${coveoConfig.coveoSearchHub}"
109
- coveo-object-type="${coveoConfig.coveoObjectType}"
110
- coveo-language="${coveoConfig.coveoLanguage}"
111
- coveo-release="${coveoConfig.coveoRelease}"
112
- coveo-deliverable="${coveoConfig.coveoDeliverable}"
138
+ coveo-organization-id="${coveoXMLDocsConfig.coveoOrganizationId}"
139
+ coveo-public-access-token="${coveoXMLDocsConfig.coveoPublicAccessToken}"
140
+ coveo-search-hub="${coveoXMLDocsConfig.coveoSearchHub}"
141
+ coveo-advanced-query-config="${coveoXMLDocsConfig.coveoAdvancedQueryConfig}"
113
142
  ></dx-sidebar>
114
143
  </div>
115
144
  `;
@@ -122,13 +151,10 @@ export const WithBadges = (args: any) => html`
122
151
  header="${args.header}"
123
152
  trees="${JSON.stringify(SAMPLE_TREES_WITH_METHODS)}"
124
153
  value="${args.value}"
125
- coveo-organization-id="${coveoConfig.coveoOrganizationId}"
126
- coveo-public-access-token="${coveoConfig.coveoPublicAccessToken}"
127
- coveo-search-hub="${coveoConfig.coveoSearchHub}"
128
- coveo-object-type="${coveoConfig.coveoObjectType}"
129
- coveo-language="${coveoConfig.coveoLanguage}"
130
- coveo-release="${coveoConfig.coveoRelease}"
131
- coveo-deliverable="${coveoConfig.coveoDeliverable}"
154
+ coveo-organization-id="${coveoXMLDocsConfig.coveoOrganizationId}"
155
+ coveo-public-access-token="${coveoXMLDocsConfig.coveoPublicAccessToken}"
156
+ coveo-search-hub="${coveoXMLDocsConfig.coveoSearchHub}"
157
+ coveo-advanced-query-config="${coveoXMLDocsConfig.coveoAdvancedQueryConfig}"
132
158
  ></dx-sidebar>
133
159
  </div>
134
160
  `;
@@ -146,13 +172,10 @@ export const PositionSticky = ({ header, trees, value }: any) => html`
146
172
  header="${header}"
147
173
  trees="${JSON.stringify(trees)}"
148
174
  value="${value}"
149
- coveo-organization-id="${coveoConfig.coveoOrganizationId}"
150
- coveo-public-access-token="${coveoConfig.coveoPublicAccessToken}"
151
- coveo-search-hub="${coveoConfig.coveoSearchHub}"
152
- coveo-object-type="${coveoConfig.coveoObjectType}"
153
- coveo-language="${coveoConfig.coveoLanguage}"
154
- coveo-release="${coveoConfig.coveoRelease}"
155
- coveo-deliverable="${coveoConfig.coveoDeliverable}"
175
+ coveo-organization-id="${coveoXMLDocsConfig.coveoOrganizationId}"
176
+ coveo-public-access-token="${coveoXMLDocsConfig.coveoPublicAccessToken}"
177
+ coveo-search-hub="${coveoXMLDocsConfig.coveoSearchHub}"
178
+ coveo-advanced-query-config="${coveoXMLDocsConfig.coveoAdvancedQueryConfig}"
156
179
  ></dx-sidebar>
157
180
  `;
158
181
 
@@ -177,13 +200,10 @@ export const UseSlot = ({ header, trees, value }: any) => html`
177
200
  header="${header}"
178
201
  trees="${JSON.stringify(trees)}"
179
202
  value="${value}"
180
- coveo-organization-id="${coveoConfig.coveoOrganizationId}"
181
- coveo-public-access-token="${coveoConfig.coveoPublicAccessToken}"
182
- coveo-search-hub="${coveoConfig.coveoSearchHub}"
183
- coveo-object-type="${coveoConfig.coveoObjectType}"
184
- coveo-language="${coveoConfig.coveoLanguage}"
185
- coveo-release="${coveoConfig.coveoRelease}"
186
- coveo-deliverable="${coveoConfig.coveoDeliverable}"
203
+ coveo-organization-id="${coveoXMLDocsConfig.coveoOrganizationId}"
204
+ coveo-public-access-token="${coveoXMLDocsConfig.coveoPublicAccessToken}"
205
+ coveo-search-hub="${coveoXMLDocsConfig.coveoSearchHub}"
206
+ coveo-advanced-query-config="${coveoXMLDocsConfig.coveoAdvancedQueryConfig}"
187
207
  >
188
208
  <span slot="header">I'm in the sidebar</span>
189
209
  </dx-sidebar>
@@ -1,8 +1,9 @@
1
1
  import cx from "classnames";
2
- import { track } from "dx/instrumentation";
3
- import { LightningElement, api } from "lwc";
2
+ import { track as trackAnalytics } from "dx/instrumentation";
3
+ import { LightningElement, api, track } from "lwc";
4
4
  import { Result } from "@coveo/headless";
5
5
  import { TreeNode, SidebarGtmAction } from "typings/custom";
6
+ import { getSidebarSearchParams } from "utils/coveo";
6
7
  import { toJson } from "utils/normalizers";
7
8
 
8
9
  const MOBILE_SIZE_MATCH = "768px";
@@ -12,10 +13,7 @@ export default class Sidebar extends LightningElement {
12
13
  @api coveoOrganizationId!: string;
13
14
  @api coveoPublicAccessToken!: string;
14
15
  @api coveoSearchHub!: string;
15
- @api coveoObjectType!: string;
16
- @api coveoLanguage!: string;
17
- @api coveoRelease!: string;
18
- @api coveoDeliverable!: string;
16
+ @api coveoAdvancedQueryConfig!: string;
19
17
  @api header: string = "";
20
18
 
21
19
  @api
@@ -49,15 +47,20 @@ export default class Sidebar extends LightningElement {
49
47
  private expanded: boolean = true;
50
48
  private _toggleHovered: boolean = false;
51
49
  private _value?: string = undefined;
50
+
51
+ @track
52
52
  private _trees!: Array<TreeNode>;
53
53
 
54
54
  private mobile: boolean = true;
55
55
  private matchMedia!: MediaQueryList;
56
56
  private valueToLabel: { [key: string]: string } = {};
57
57
 
58
- private isSearchLoading: boolean = false;
58
+ private isSearchLoading: boolean = true;
59
59
  private searchValue: string | null = null;
60
60
  private searchResults: Result[] = [];
61
+ private scrollToSelectedSearchResult: boolean = false;
62
+ private selectedSearchResultIndex: number = -1;
63
+ private requestedFetchMoreResults: boolean = false;
61
64
 
62
65
  private get areResultsEmpty(): boolean {
63
66
  return (
@@ -76,14 +79,37 @@ export default class Sidebar extends LightningElement {
76
79
  }
77
80
 
78
81
  private get isUserSearching(): boolean {
79
- return !!this.searchValue;
82
+ return !!this.searchValue || this.isSearchLoading;
83
+ }
84
+
85
+ private get showSearchResults(): boolean {
86
+ return (
87
+ !!this.searchValue && !this.isSearchLoading && !this.areResultsEmpty
88
+ );
89
+ }
90
+
91
+ private get isNearBottomOfSearchResults(): boolean {
92
+ const scrollArea = this.template.querySelector(
93
+ ".sidebar-content-search"
94
+ );
95
+ if (!scrollArea) {
96
+ return false;
97
+ }
98
+
99
+ const { scrollHeight, scrollTop } = scrollArea;
100
+ const { height } = scrollArea?.getBoundingClientRect();
101
+
102
+ return scrollHeight - scrollTop - height < 300;
80
103
  }
81
104
 
82
105
  private get containerClass() {
83
106
  return cx("container", {
84
107
  collapsed: !this.expanded,
85
108
  "container-border-active": this._toggleHovered,
86
- "container-mobile": this.mobile
109
+ "container-mobile": this.mobile,
110
+ "user-searching": this.isUserSearching,
111
+ "show-tree": !this.isUserSearching,
112
+ "search-empty": this.isSearchEmpty
87
113
  });
88
114
  }
89
115
 
@@ -95,6 +121,19 @@ export default class Sidebar extends LightningElement {
95
121
  );
96
122
  }
97
123
 
124
+ constructor() {
125
+ super();
126
+
127
+ if (getSidebarSearchParams()) {
128
+ this.isSearchLoading = true;
129
+ this.scrollToSelectedSearchResult = true;
130
+ }
131
+ }
132
+
133
+ renderedCallback() {
134
+ this.initializeSearchScrollPosition();
135
+ }
136
+
98
137
  connectedCallback() {
99
138
  this.matchMedia = window.matchMedia(
100
139
  `(max-width: ${MOBILE_SIZE_MATCH})`
@@ -122,7 +161,7 @@ export default class Sidebar extends LightningElement {
122
161
  const sideNavAction: SidebarGtmAction = this.expanded
123
162
  ? "expand"
124
163
  : "collapse";
125
- track(event.currentTarget!, "custEv_leftNavLinkClick", {
164
+ trackAnalytics(event.currentTarget!, "custEv_leftNavLinkClick", {
126
165
  sideNavAction,
127
166
  clickText: TOGGLE_BUTTON_LABEL
128
167
  });
@@ -141,14 +180,53 @@ export default class Sidebar extends LightningElement {
141
180
  }
142
181
 
143
182
  private onSearchChange(e: CustomEvent) {
183
+ this.requestedFetchMoreResults = false;
144
184
  this.searchResults = e.detail.results;
145
185
  this.searchValue = e.detail.value;
146
186
  }
147
187
 
148
188
  private onSearchLoading(e: CustomEvent) {
149
- this.isSearchLoading = e.detail;
189
+ if (!e.detail.loading && this.scrollToSelectedSearchResult) {
190
+ this.selectedSearchResultIndex = this.searchResults.findIndex(
191
+ (r) => r.selected
192
+ );
193
+ }
194
+ this.isSearchLoading = e.detail.firstLoad;
150
195
  }
151
196
 
197
+ private onSidebarSearchContentScroll() {
198
+ if (this.isSearchLoading) {
199
+ return;
200
+ }
201
+
202
+ const search = this.template.querySelector("dx-sidebar-search");
203
+
204
+ if (this.isNearBottomOfSearchResults && search && !this.requestedFetchMoreResults) {
205
+ this.requestedFetchMoreResults = true;
206
+ search.fetchMoreResults();
207
+ }
208
+ }
209
+
210
+ private initializeSearchScrollPosition = () => {
211
+ if (
212
+ this.scrollToSelectedSearchResult &&
213
+ this.selectedSearchResultIndex >= 0
214
+ ) {
215
+ const selectedResult = this.template.querySelector(
216
+ `dx-sidebar-search-result:nth-child(${
217
+ this.selectedSearchResultIndex + 1
218
+ })`
219
+ );
220
+ if (!selectedResult || !selectedResult.parentNode) {
221
+ return;
222
+ }
223
+ (selectedResult.parentNode as HTMLElement).scrollTop =
224
+ selectedResult.offsetTop -
225
+ (selectedResult.parentNode as HTMLElement).offsetTop;
226
+ this.scrollToSelectedSearchResult = false;
227
+ }
228
+ };
229
+
152
230
  private assignValueToLabel(node: TreeNode): void {
153
231
  this.valueToLabel[node.name] = node.label;
154
232
  if (node.children && node.children.length > 0) {