@salesforcedevs/dx-components 1.3.251 → 1.3.253

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 (30) hide show
  1. package/lwc.config.json +1 -0
  2. package/package.json +2 -2
  3. package/src/modules/dx/button/button.html +2 -2
  4. package/src/modules/dx/button/button.ts +1 -1
  5. package/src/modules/dx/dropdownOption/dropdownOption.css +5 -3
  6. package/src/modules/dx/headerNav/headerNav.css +9 -0
  7. package/src/modules/dx/popover/popover.css +2 -2
  8. package/src/modules/dx/searchResults/searchResults.css +5 -0
  9. package/src/modules/dx/searchResults/searchResults.ts +65 -1
  10. package/src/modules/dx/sidebar/sidebar.css +9 -5
  11. package/src/modules/dx/sidebar/sidebar.html +32 -16
  12. package/src/modules/dx/sidebar/sidebar.ts +2 -42
  13. package/src/modules/dx/sidebarFooterNav/sidebarFooterNav.css +49 -0
  14. package/src/modules/dx/sidebarFooterNav/sidebarFooterNav.html +55 -0
  15. package/src/modules/dx/sidebarFooterNav/sidebarFooterNav.ts +106 -0
  16. package/src/modules/dx/sidebarOld/sidebarOld.css +1 -0
  17. package/src/modules/dx/sidebarOld/sidebarOld.html +31 -14
  18. package/src/modules/dx/sidebarOld/sidebarOld.ts +4 -38
  19. package/src/modules/dx/sidebarSearchResult/sidebarSearchResult.css +35 -4
  20. package/src/modules/dx/sidebarSearchResult/sidebarSearchResult.html +16 -10
  21. package/src/modules/dx/tab/tab.css +48 -19
  22. package/src/modules/dx/treeItem/treeItem.css +2 -2
  23. package/src/modules/dx/treeTile/treeTile.css +39 -28
  24. package/src/modules/dx/treeTile/treeTile.html +6 -6
  25. package/src/modules/dx/treeTile/treeTile.ts +2 -1
  26. package/src/modules/dxBaseElements/sidebarBase/sidebarBase.ts +100 -1
  27. package/src/modules/dxConstants/contentTypes/contentTypes.ts +8 -2
  28. package/src/modules/dxHelpers/commonHeader/commonHeader.css +4 -2
  29. package/src/modules/dxHelpers/commonSidebar/commonSidebar.css +38 -16
  30. package/src/modules/dxHelpers/commonTreeItem/commonTreeItem.css +26 -3
package/lwc.config.json CHANGED
@@ -78,6 +78,7 @@
78
78
  "dx/select",
79
79
  "dx/sidebar",
80
80
  "dx/sidebarOld",
81
+ "dx/sidebarFooterNav",
81
82
  "dx/skipNavLink",
82
83
  "dx/socials",
83
84
  "dx/spinner",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforcedevs/dx-components",
3
- "version": "1.3.251",
3
+ "version": "1.3.253",
4
4
  "description": "DX Lightning web components",
5
5
  "license": "MIT",
6
6
  "engines": {
@@ -45,5 +45,5 @@
45
45
  "volta": {
46
46
  "node": "16.19.1"
47
47
  },
48
- "gitHead": "25314860cd3d24abee849fe5dff1336780904dc9"
48
+ "gitHead": "20e30001e8423cd101d5ac0e94fff7d669cf9ef1"
49
49
  }
@@ -7,7 +7,7 @@
7
7
  aria-label={ariaLabel}
8
8
  part="container"
9
9
  >
10
- <span if:false={loading}>
10
+ <span if:false={loading} part="content">
11
11
  <slot onslotchange={onSlotChange}></slot>
12
12
  </span>
13
13
  <dx-icon
@@ -29,7 +29,7 @@
29
29
  aria-label={ariaLabel}
30
30
  part="container"
31
31
  >
32
- <span if:false={loading}>
32
+ <span if:false={loading} part="content">
33
33
  <slot onslotchange={onSlotChange}></slot>
34
34
  </span>
35
35
  <dx-icon
@@ -80,6 +80,6 @@ export default class Button extends LightningElement {
80
80
 
81
81
  private onSlotChange(e: LightningSlotElement) {
82
82
  const slot = e.target;
83
- this.isSlotEmpty = slot.assignedElements().length !== 0;
83
+ this.isSlotEmpty = slot.assignedNodes().length === 0;
84
84
  }
85
85
  }
@@ -38,7 +38,7 @@
38
38
  }
39
39
 
40
40
  .option:active {
41
- background: var(--dx-g-blue-vibrant-95) !important;
41
+ background: var(--dx-g-cloud-blue-vibrant-95) !important;
42
42
  }
43
43
 
44
44
  .option:not(.option-active):hover {
@@ -46,7 +46,9 @@
46
46
  }
47
47
 
48
48
  .option-active {
49
- background: var(--dx-g-blue-vibrant-95);
49
+ --dx-c-dropdown-option-label-color: var(--dx-g-blue-vibrant-50);
50
+
51
+ background: var(--dx-g-cloud-blue-vibrant-95);
50
52
  }
51
53
 
52
54
  .option_details {
@@ -67,7 +69,7 @@
67
69
  color: var(--dx-c-dropdown-option-label-color, var(--dx-g-blue-vibrant-50));
68
70
  display: flex;
69
71
  align-items: center;
70
- font-weight: var(--dx-g-font-bold);
72
+ font-weight: var(--dx-c-dropdown-option-font-weight, var(--dx-g-font-bold));
71
73
  font-size: var(--dx-c-dropdown-option-font-size, var(--dx-g-text-base));
72
74
  }
73
75
 
@@ -10,17 +10,26 @@ nav {
10
10
 
11
11
  .nav-list {
12
12
  display: flex;
13
+ margin-top: calc(var(--dx-g-spacing-2xs) + 1px);
13
14
  }
14
15
 
15
16
  .nav-list li {
16
17
  position: relative;
17
18
  }
18
19
 
20
+ .nav-list li:not(:last-child) {
21
+ margin-right: var(--dx-g-spacing-md);
22
+ }
23
+
19
24
  .nav-list-item-nav-menu {
20
25
  display: none;
21
26
  }
22
27
 
23
28
  @media (max-width: 768px) {
29
+ .nav-list {
30
+ margin-top: 0;
31
+ }
32
+
24
33
  .nav-list-item-nav-menu {
25
34
  display: flex;
26
35
  }
@@ -28,7 +28,7 @@
28
28
  max-height: 65vh;
29
29
  padding: var(--popover-padding);
30
30
  overflow-y: auto;
31
- transition: opacity 0.2s linear, transform 0.2s linear;
31
+ transition: var(--popover-transition);
32
32
  transition-delay: 0.02s;
33
33
  transform: translateY(var(--dx-g-spacing-xs));
34
34
  opacity: 0;
@@ -41,7 +41,7 @@
41
41
 
42
42
  .popover-container_open .popover {
43
43
  opacity: 1;
44
- transform: translateY(0);
44
+ transform: var(--popover-container-open-transform);
45
45
  }
46
46
 
47
47
  .popover-overridewidth {
@@ -284,6 +284,7 @@ li.coveo-dynamic-facet-breadcrumb-value-list-item {
284
284
  }
285
285
 
286
286
  .dx-result-excerpt {
287
+ display: inline;
287
288
  color: var(--dx-g-gray-10);
288
289
  font-size: 14px;
289
290
  line-height: 20px;
@@ -465,6 +466,10 @@ li.coveo-dynamic-facet-breadcrumb-value-list-item {
465
466
  --dx-c-stroke-width: 1px;
466
467
  }
467
468
 
469
+ .post-info-container {
470
+ margin-top: var(--dx-g-spacing-smd);
471
+ }
472
+
468
473
  @media screen and (max-width: 768px) {
469
474
  .no-results > img {
470
475
  display: none;
@@ -1,5 +1,6 @@
1
1
  import { LightningElement, api, track } from "lwc";
2
2
  import type * as CoveoSDK from "coveo-search-ui";
3
+ import { DateTime } from "luxon";
3
4
  import { track as trackGTM } from "dxUtils/analytics";
4
5
  import {
5
6
  CONTENT_TYPE_LABELS,
@@ -72,7 +73,17 @@ const resultsTemplatesInnerHtml = `
72
73
  <svg xmlns="http://www.w3.org/2000/svg" style="display: inline; vertical-align: baseline;" fill="var(--dx-g-blue-vibrant-20)" width="20" height="20" part="svg" aria-hidden="true"><use xlink:href="/assets/icons/utility-sprite/svg/symbols.svg#new_window"></use></svg>
73
74
  <% } %>
74
75
  </a>
76
+ <span class="CoveoFieldValue" data-field="@sflastmodifieddate" data-helper="postedDate" data-html-value="true"></span>
75
77
  <p class="dx-result-excerpt CoveoExcerpt"></p>
78
+ <% if (raw.sfcommentcount || raw.sflikecount) { %>
79
+ <div class="post-info-container">
80
+ <span class="CoveoFieldValue" data-field="@sfcommentcount" data-helper="replies" data-html-value="true"></span>
81
+ <% if (raw.sfcommentcount && raw.sflikecount) { %>
82
+ <span>&bull;</span>
83
+ <% } %>
84
+ <span class="CoveoFieldValue" data-field="@sflikecount" data-helper="likes" data-html-value="true"></span>
85
+ </div>
86
+ <% } %>
76
87
  </div>
77
88
  </script>
78
89
  <script
@@ -109,7 +120,17 @@ const resultsTemplatesInnerHtml = `
109
120
  <svg xmlns="http://www.w3.org/2000/svg" style="display: inline; vertical-align: baseline;" fill="var(--dx-g-blue-vibrant-20)" width="20" height="20" part="svg" aria-hidden="true"><use xlink:href="/assets/icons/utility-sprite/svg/symbols.svg#new_window"></use></svg>
110
121
  <% } %>
111
122
  </a>
123
+ <span class="CoveoFieldValue" data-field="@sflastmodifieddate" data-helper="postedDate" data-html-value="true"></span>
112
124
  <p class="dx-result-excerpt CoveoExcerpt"></p>
125
+ <% if (raw.sfcommentcount || raw.sflikecount) { %>
126
+ <div class="post-info-container">
127
+ <span class="CoveoFieldValue" data-field="@sfcommentcount" data-helper="replies" data-html-value="true"></span>
128
+ <% if (raw.sfcommentcount && raw.sflikecount) { %>
129
+ <span>&bull;</span>
130
+ <% } %>
131
+ <span class="CoveoFieldValue" data-field="@sflikecount" data-helper="likes" data-html-value="true"></span>
132
+ </div>
133
+ <% } %>
113
134
  </div>
114
135
  </script>
115
136
  `;
@@ -122,7 +143,15 @@ const isTrailheadDomain = (domain: string) =>
122
143
  domain === "trailhead.salesforce.com" ||
123
144
  domain === "dev.trailhead.salesforce.com";
124
145
 
125
- const buildTemplateHelperBadge = (value: keyof typeof CONTENT_TYPE_LABELS) => {
146
+ const buildTemplateHelperBadge = (
147
+ rawValue: keyof typeof CONTENT_TYPE_LABELS | "community posts"
148
+ ) => {
149
+ let value = rawValue;
150
+
151
+ if (value === "community posts") {
152
+ value = "community";
153
+ }
154
+
126
155
  const style = getContentTypeColorVariables(value);
127
156
  const label = CONTENT_TYPE_LABELS[value];
128
157
  const { iconSprite, iconSymbol } = CONTENT_TYPE_ICONS[value];
@@ -242,6 +271,26 @@ const buildTemplateHelperMetaBreadcrumbs = (value: string) => {
242
271
  `;
243
272
  };
244
273
 
274
+ const buildTemplateHelperPostedDate = (value: string) => {
275
+ const time = DateTime.fromMillis(Number(value)).toLocaleString(
276
+ DateTime.DATE_MED
277
+ );
278
+
279
+ return `Posted on: ${time} - `;
280
+ };
281
+
282
+ const buildTemplateHelperReplies = (value: string) => {
283
+ const number = Number(value);
284
+
285
+ return `<strong>${value} ${number > 1 ? "Replies" : "Reply"}</strong>`;
286
+ };
287
+
288
+ const buildTemplateHelperLikes = (value: string) => {
289
+ const number = Number(value);
290
+
291
+ return `<strong>${value} ${number > 1 ? "Likes" : "Like"}</strong>`;
292
+ };
293
+
245
294
  // @ts-ignore Dark Magic (TM) we are overriding the 'title' field with a custom getter. We should really stop doing this.
246
295
  export default class SearchResults extends LightningElement {
247
296
  @api coveoOrganizationId!: string;
@@ -521,6 +570,21 @@ export default class SearchResults extends LightningElement {
521
570
  buildTemplateHelperUriBreadcrumbs
522
571
  );
523
572
 
573
+ Coveo.TemplateHelpers.registerTemplateHelper(
574
+ "postedDate",
575
+ buildTemplateHelperPostedDate
576
+ );
577
+
578
+ Coveo.TemplateHelpers.registerTemplateHelper(
579
+ "replies",
580
+ buildTemplateHelperReplies
581
+ );
582
+
583
+ Coveo.TemplateHelpers.registerTemplateHelper(
584
+ "likes",
585
+ buildTemplateHelperLikes
586
+ );
587
+
524
588
  Coveo.init(this.root);
525
589
  }
526
590
 
@@ -15,7 +15,7 @@ dx-empty-state {
15
15
 
16
16
  .sidebar-content {
17
17
  overflow-y: auto;
18
- padding-bottom: var(--dx-g-spacing-md);
18
+ padding: var(--dx-g-spacing-sm) 0 var(--dx-g-spacing-2xl);
19
19
  }
20
20
 
21
21
  .loading-skeleton {
@@ -53,6 +53,14 @@ dx-empty-state {
53
53
  --dx-c-button-secondary-color-hover: var(--dx-g-gray-80);
54
54
  }
55
55
 
56
+ .results-heading {
57
+ --dx-g-text-body-color: var(--sds-g-gray-14);
58
+
59
+ margin-bottom: var(--dx-g-spacing-sm);
60
+ padding-top: var(--dx-g-spacing-smd);
61
+ font-weight: var(--dx-g-font-bold);
62
+ }
63
+
56
64
  @media (max-width: 768px) {
57
65
  :host {
58
66
  width: 100%;
@@ -61,8 +69,4 @@ dx-empty-state {
61
69
  .loading-skeleton {
62
70
  width: 100%;
63
71
  }
64
-
65
- .sidebar-content-tree {
66
- padding: 0 var(--dx-g-spacing-sm);
67
- }
68
72
  }
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div class={containerClass} part="container">
3
3
  <div
4
- if:true={mobile}
4
+ lwc:if={mobile}
5
5
  class="header padding-horizontal"
6
6
  onclick={onToggleClick}
7
7
  >
@@ -16,22 +16,17 @@
16
16
  <dx-icon
17
17
  sprite="utility"
18
18
  symbol={menuSymbol}
19
- color="blue-vibrant-20"
19
+ color="blue-vibrant-50"
20
+ size="medium"
20
21
  ></dx-icon>
21
22
  </dx-button>
22
23
  </div>
23
24
  </div>
24
-
25
- <div class="sidebar-header padding-horizontal">
26
- <div class="header" if:false={mobile}>
25
+ <div class="sidebar-header" show-shadow={showBoxShadow}>
26
+ <div class="header padding-horizontal" lwc:if={isDesktop}>
27
27
  <h2 class="dx-text-display-6 header-title">{header}</h2>
28
- <slot name="header"></slot>
29
28
  </div>
30
- <div class="mobile-header" if:true={mobile}>
31
- <h2 class="dx-text-display-6">{header}</h2>
32
- <slot name="header"></slot>
33
- </div>
34
- <div class="search">
29
+ <div class="search padding-horizontal">
35
30
  <dx-sidebar-search
36
31
  onchange={onSearchChange}
37
32
  onloading={onSearchLoading}
@@ -41,9 +36,10 @@
41
36
  coveo-advanced-query-config={coveoAdvancedQueryConfig}
42
37
  ></dx-sidebar-search>
43
38
  </div>
39
+ <slot name="version-picker"></slot>
44
40
  <h2
45
- class="results-heading dx-text-display-8"
46
- if:true={showResultsHeading}
41
+ class="results-heading dx-text-body-3 padding-horizontal"
42
+ lwc:if={showResultsHeading}
47
43
  >
48
44
  Results
49
45
  </h2>
@@ -53,13 +49,13 @@
53
49
  onscroll={onSidebarSearchContentScroll}
54
50
  >
55
51
  <img
56
- if:true={isSearchLoading}
52
+ lwc:if={isSearchLoading}
57
53
  class="loading-skeleton padding-horizontal"
58
54
  src="https://a.sfdcstatic.com/developer-website/images/sidebar-loading.svg"
59
55
  alt="loading"
60
56
  />
61
57
  <template
62
- if:true={showSearchResults}
58
+ lwc:if={showSearchResults}
63
59
  for:each={searchResults}
64
60
  for:item="result"
65
61
  >
@@ -83,7 +79,7 @@
83
79
  size="small"
84
80
  ></dx-empty-state>
85
81
  </div>
86
- <div class="sidebar-content sidebar-content-tree">
82
+ <div class={sidebarContentClass} onscroll={handleScroll}>
87
83
  <dx-tree
88
84
  for:each={trees}
89
85
  for:item="tree"
@@ -95,5 +91,25 @@
95
91
  onselecteditemrendered={onSelectedItemRendered}
96
92
  ></dx-tree>
97
93
  </div>
94
+ <template lwc:if={mobile}>
95
+ <div lwc:if={hasMobileSidebarFooter} class="footer-nav">
96
+ <dx-sidebar-footer-nav
97
+ lang-value-path={langValuePath}
98
+ language={language}
99
+ languages={languages}
100
+ ></dx-sidebar-footer-nav>
101
+ </div>
102
+ </template>
103
+ <template lwc:else>
104
+ <div lwc:if={hasSidebarFooter} class="footer-nav">
105
+ <dx-sidebar-footer-nav
106
+ lang-value-path={langValuePath}
107
+ language={language}
108
+ languages={languages}
109
+ bail-href={bailHref}
110
+ bail-label={bailLabel}
111
+ ></dx-sidebar-footer-nav>
112
+ </div>
113
+ </template>
98
114
  </div>
99
115
  </template>
@@ -15,15 +15,6 @@ export default class Sidebar extends SidebarBase {
15
15
  @api coveoAdvancedQueryConfig!: string;
16
16
  @api header: string = "";
17
17
 
18
- @api
19
- get value() {
20
- return this._value;
21
- }
22
-
23
- set value(value) {
24
- this._value = value;
25
- }
26
-
27
18
  @api
28
19
  get trees() {
29
20
  return this._trees;
@@ -52,16 +43,10 @@ export default class Sidebar extends SidebarBase {
52
43
  )?.setInputValue(searchTerm);
53
44
  }
54
45
 
55
- private expanded: boolean = true;
56
- private _value?: string = undefined;
57
-
58
46
  @track
59
47
  private _trees!: Array<TreeNode>;
60
-
61
- private mobile: boolean = true;
62
48
  private matchMedia!: MediaQueryList;
63
49
  private valueToLabel: { [key: string]: string } = {};
64
-
65
50
  private isSearchLoading: boolean = false;
66
51
  private searchValue: string | null = null;
67
52
  private searchResults: SidebarSearchResult[] = [];
@@ -171,32 +156,6 @@ export default class Sidebar extends SidebarBase {
171
156
  this.matchMedia.removeEventListener("change", this.onMediaChange);
172
157
  }
173
158
 
174
- private onMediaChange = (event: MediaQueryListEvent | MediaQueryList) => {
175
- this.mobile = event.matches;
176
- this.expanded = !this.mobile;
177
- };
178
-
179
- private onSelect(event: CustomEvent) {
180
- this._value = event.detail.name;
181
-
182
- if (this.mobile) {
183
- this.onToggleClick();
184
- }
185
- }
186
-
187
- private onToggleClick() {
188
- this.expanded = !this.expanded;
189
- this.dispatchEvent(
190
- new CustomEvent("togglesidebar", {
191
- detail: {
192
- open: this.expanded,
193
- bubbles: true,
194
- composed: true
195
- }
196
- })
197
- );
198
- }
199
-
200
159
  private makeKey(): string {
201
160
  return Math.random().toString(36).substring(7);
202
161
  }
@@ -216,7 +175,7 @@ export default class Sidebar extends SidebarBase {
216
175
  this.isSearchLoading = e.detail;
217
176
  }
218
177
 
219
- private onSidebarSearchContentScroll() {
178
+ private onSidebarSearchContentScroll(scrollEvent) {
220
179
  if (this.isSearchLoading) {
221
180
  return;
222
181
  }
@@ -233,6 +192,7 @@ export default class Sidebar extends SidebarBase {
233
192
  this.requestedFetchMoreResults = true;
234
193
  search.fetchMoreResults();
235
194
  }
195
+ this.handleScroll(scrollEvent);
236
196
  }
237
197
 
238
198
  private initializeSearchScrollPosition() {
@@ -0,0 +1,49 @@
1
+ @import "dxHelpers/reset";
2
+
3
+ :host {
4
+ --button-primary-color: var(--dx-g-blue-vibrant-50);
5
+ --button-primary-color-hover: var(--dx-g-cloud-blue-vibrant-95);
6
+ --popover-container-open-transform: translateX(-4px) translateY(-8px);
7
+ --popover-transition: none;
8
+ --dx-c-dropdown-option-font-weight: normal;
9
+ --dx-c-dropdown-option-label-color: var(--dx-g-gray-10);
10
+ }
11
+
12
+ .footer-item {
13
+ display: flex;
14
+ height: var(--dx-g-spacing-xl);
15
+ align-items: center;
16
+ border-radius: var(--dx-g-spacing-xs);
17
+ }
18
+
19
+ .footer-display {
20
+ display: flex;
21
+ }
22
+
23
+ .footer_lang-dropdown {
24
+ --dx-c-button-primary-color: var(--button-primary-color);
25
+ --dx-c-button-secondary-color-hover: var(--button-primary-color-hover);
26
+ --border-color: var(--button-primary-color);
27
+ }
28
+
29
+ .footer_lang-dropdown[aria-expanded="true"] {
30
+ --dx-c-button-secondary-color-hover: var(--dx-g-cloud-blue-vibrant-95);
31
+ --dx-c-button-primary-color: var(--dx-g-blue-vibrant-40);
32
+ }
33
+
34
+ .footer_lang-dropdown:hover,
35
+ .footer_lang-dropdown:active,
36
+ .pdf_bail-link:hover {
37
+ /* background-color: var(--button-primary-color-hover);
38
+
39
+ --border-color: var(--button-primary-color-hover); */
40
+ --dx-c-button-secondary-color-hover: var(--dx-g-cloud-blue-vibrant-95);
41
+ --dx-c-button-primary-color: var(--dx-g-blue-vibrant-40);
42
+ }
43
+
44
+ .pdf_bail-link {
45
+ --dx-c-button-secondary-color-hover: var(--button-primary-color-hover);
46
+ --dx-c-button-primary-color: var(--button-primary-color);
47
+
48
+ margin-right: var(--dx-g-spacing-sm);
49
+ }
@@ -0,0 +1,55 @@
1
+ <template>
2
+ <div lwc:if={mobile} class="footer-display">
3
+ <dx-dropdown
4
+ lwc:if={hasLanguages}
5
+ options={languages}
6
+ small
7
+ value-path={langValuePath}
8
+ value={language}
9
+ onchange={onLangChange}
10
+ >
11
+ <dx-button
12
+ class="footer-item footer_lang-dropdown"
13
+ aria-label="Select Language"
14
+ variant="tertiary"
15
+ icon-size="small"
16
+ icon-symbol="world"
17
+ >
18
+ {languageLabel}
19
+ </dx-button>
20
+ </dx-dropdown>
21
+ </div>
22
+ <div lwc:else class="footer-display">
23
+ <dx-button
24
+ lwc:if={hasBailLink}
25
+ aria-label={bailLabel}
26
+ class="footer-item pdf_bail-link"
27
+ href={bailHref}
28
+ onclick={handleBailClick}
29
+ variant="tertiary"
30
+ icon-size="medium"
31
+ icon-symbol="new_window"
32
+ target="_blank"
33
+ >
34
+ {bailLabel}
35
+ </dx-button>
36
+ <dx-dropdown
37
+ lwc:if={hasLanguages}
38
+ options={languages}
39
+ small
40
+ value-path={langValuePath}
41
+ value={language}
42
+ onchange={onLangChange}
43
+ >
44
+ <dx-button
45
+ class="footer-item footer_lang-dropdown"
46
+ aria-label="Select Language"
47
+ variant="tertiary"
48
+ icon-size="medium"
49
+ icon-symbol="world"
50
+ >
51
+ {languageLabel}
52
+ </dx-button>
53
+ </dx-dropdown>
54
+ </div>
55
+ </template>
@@ -0,0 +1,106 @@
1
+ /* eslint-disable @lwc/lwc/no-document-query */
2
+ import { LightningElement, api } from "lwc";
3
+ import type { OptionWithLink } from "typings/custom";
4
+ import { toJson } from "dxUtils/normalizers";
5
+ import get from "lodash.get";
6
+ import { track } from "dxUtils/analytics";
7
+
8
+ const MOBILE_SIZE_MATCH = "768px";
9
+
10
+ export default class SidebarFooterNav extends LightningElement {
11
+ @api langValuePath: string = "id";
12
+ @api bailHref?: string | null = null;
13
+ @api bailLabel?: string | null = null;
14
+
15
+ @api
16
+ get languages() {
17
+ return this._languages;
18
+ }
19
+
20
+ set languages(value) {
21
+ this._languages = toJson(value);
22
+ }
23
+
24
+ @api
25
+ get language() {
26
+ return this._language;
27
+ }
28
+
29
+ set language(value) {
30
+ this._language = value;
31
+ }
32
+
33
+ private _languages!: OptionWithLink[];
34
+ private _language: string | null = null;
35
+ private mobile: boolean = false;
36
+ private matchMedia!: MediaQueryList;
37
+
38
+ private get hasLanguages(): boolean {
39
+ return this.languages?.length > 1;
40
+ }
41
+
42
+ private get languageLabel(): string {
43
+ const matchingLanguageData = this.language
44
+ ? this.languages.find(
45
+ (lang) => get(lang, this.langValuePath) === this.language
46
+ )
47
+ : null;
48
+ return matchingLanguageData?.label || this.languages[0].label;
49
+ }
50
+
51
+ get hasBailLink(): boolean {
52
+ return !!(this.bailHref && this.bailLabel);
53
+ }
54
+
55
+ private onLangChange(event: CustomEvent<string>): void {
56
+ const { detail } = event;
57
+ this._language = detail;
58
+
59
+ this.dispatchEvent(
60
+ new CustomEvent("langchange", {
61
+ detail,
62
+ bubbles: true,
63
+ composed: true
64
+ })
65
+ );
66
+ }
67
+
68
+ private getFilename = function (path: string) {
69
+ return path.substring(path.lastIndexOf("/") + 1);
70
+ };
71
+
72
+ private handleBailClick(event: Event) {
73
+ const payload = {
74
+ click_text: "pdf",
75
+ click_url: this.bailHref,
76
+ element_title: "pdf",
77
+ element_type: "link",
78
+ content_category: "download"
79
+ };
80
+ track(event.target!, "custEv_pdfDownload", {
81
+ ...payload,
82
+ file_name: this.getFilename(this.bailHref!),
83
+ file_extension: "pdf"
84
+ });
85
+
86
+ track(event.target!, "custEv_linkClick", {
87
+ ...payload
88
+ });
89
+ }
90
+
91
+ connectedCallback() {
92
+ this.matchMedia = window.matchMedia(
93
+ `(max-width: ${MOBILE_SIZE_MATCH})`
94
+ );
95
+ this.onMediaChange(this.matchMedia);
96
+ this.matchMedia.addEventListener("change", this.onMediaChange);
97
+ }
98
+
99
+ disconnectedCallback() {
100
+ this.matchMedia.removeEventListener("change", this.onMediaChange);
101
+ }
102
+
103
+ private onMediaChange = (event: MediaQueryListEvent | MediaQueryList) => {
104
+ this.mobile = event.matches;
105
+ };
106
+ }
@@ -2,6 +2,7 @@
2
2
 
3
3
  .sidebar-content {
4
4
  overflow-y: auto;
5
+ padding: var(--dx-g-spacing-sm) 0 var(--dx-g-spacing-2xl);
5
6
  }
6
7
 
7
8
  dx-empty-state {