@salesforcedevs/docs-components 0.0.0 → 0.0.1-edit

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 (84) hide show
  1. package/lwc.config.json +11 -2
  2. package/package.json +14 -13
  3. package/src/modules/doc/{amfReference/utils.ts → amfModelParser/amfModelParser.ts} +10 -5
  4. package/src/modules/doc/amfReference/amfReference.css +23 -3
  5. package/src/modules/doc/amfReference/amfReference.html +34 -21
  6. package/src/modules/doc/amfReference/amfReference.ts +225 -92
  7. package/src/modules/doc/amfReference/types.ts +3 -11
  8. package/src/modules/doc/amfTopic/amfTopic.css +20 -0
  9. package/src/modules/doc/amfTopic/amfTopic.ts +35 -18
  10. package/src/modules/doc/amfTopic/types.ts +15 -13
  11. package/src/modules/doc/amfTopic/utils.ts +12 -6
  12. package/src/modules/doc/breadcrumbItem/breadcrumbItem.ts +17 -10
  13. package/src/modules/doc/breadcrumbs/breadcrumbs.html +7 -9
  14. package/src/modules/doc/breadcrumbs/breadcrumbs.ts +30 -34
  15. package/src/modules/doc/chat/README.md +179 -0
  16. package/src/modules/doc/chat/chat.css +818 -0
  17. package/src/modules/doc/chat/chat.html +241 -0
  18. package/src/modules/doc/chat/chat.ts +586 -0
  19. package/src/modules/doc/componentPlayground/componentPlayground.css +22 -0
  20. package/src/modules/doc/componentPlayground/componentPlayground.html +20 -0
  21. package/src/modules/doc/componentPlayground/componentPlayground.ts +29 -0
  22. package/src/modules/doc/content/content.css +70 -76
  23. package/src/modules/doc/content/content.html +1 -0
  24. package/src/modules/doc/content/content.ts +24 -45
  25. package/src/modules/doc/contentCallout/contentCallout.css +10 -7
  26. package/src/modules/doc/contentCallout/contentCallout.html +11 -4
  27. package/src/modules/doc/contentCallout/contentCallout.ts +8 -1
  28. package/src/modules/doc/contentLayout/contentLayout.css +1 -98
  29. package/src/modules/doc/contentLayout/contentLayout.html +37 -19
  30. package/src/modules/doc/contentLayout/contentLayout.ts +298 -89
  31. package/src/modules/doc/doDont/doDont.css +47 -0
  32. package/src/modules/doc/doDont/doDont.html +27 -0
  33. package/src/modules/doc/doDont/doDont.ts +17 -0
  34. package/src/modules/doc/editFile/editFile.css +514 -0
  35. package/src/modules/doc/editFile/editFile.html +164 -0
  36. package/src/modules/doc/editFile/editFile.ts +213 -0
  37. package/src/modules/doc/header/header.css +65 -36
  38. package/src/modules/doc/header/header.html +41 -139
  39. package/src/modules/doc/header/header.ts +54 -76
  40. package/src/modules/doc/heading/heading.css +16 -37
  41. package/src/modules/doc/heading/heading.html +4 -4
  42. package/src/modules/doc/heading/heading.ts +12 -10
  43. package/src/modules/doc/headingAnchor/headingAnchor.css +2 -2
  44. package/src/modules/doc/headingAnchor/headingAnchor.ts +2 -2
  45. package/src/modules/doc/headingContent/headingContent.css +1 -1
  46. package/src/modules/doc/headingContent/headingContent.html +2 -2
  47. package/src/modules/doc/headingContent/headingContent.ts +2 -2
  48. package/src/modules/doc/lwcContentLayout/lwcContentLayout.css +1 -0
  49. package/src/modules/doc/lwcContentLayout/lwcContentLayout.html +68 -0
  50. package/src/modules/doc/lwcContentLayout/lwcContentLayout.ts +168 -0
  51. package/src/modules/doc/overview/overview.css +40 -0
  52. package/src/modules/doc/overview/overview.html +34 -0
  53. package/src/modules/doc/overview/overview.ts +12 -0
  54. package/src/modules/doc/phase/phase.css +18 -3
  55. package/src/modules/doc/phase/phase.html +12 -2
  56. package/src/modules/doc/phase/phase.ts +44 -8
  57. package/src/modules/doc/specificationContent/specificationContent.css +36 -0
  58. package/src/modules/doc/specificationContent/specificationContent.html +171 -0
  59. package/src/modules/doc/specificationContent/specificationContent.ts +127 -0
  60. package/src/modules/doc/sprigSurvey/sprigSurvey.html +20 -0
  61. package/src/modules/doc/sprigSurvey/sprigSurvey.scoped.css +16 -0
  62. package/src/modules/doc/sprigSurvey/sprigSurvey.ts +16 -0
  63. package/src/modules/doc/toc/toc.html +1 -3
  64. package/src/modules/doc/toc/toc.ts +1 -1
  65. package/src/modules/doc/toolbar/toolbar.ts +6 -6
  66. package/src/modules/doc/versionPicker/versionPicker.css +64 -0
  67. package/src/modules/doc/versionPicker/versionPicker.html +38 -0
  68. package/src/modules/doc/versionPicker/versionPicker.ts +65 -0
  69. package/src/modules/doc/xmlContent/types.ts +9 -3
  70. package/src/modules/doc/xmlContent/utils.ts +3 -1
  71. package/src/modules/doc/xmlContent/xmlContent.css +26 -4
  72. package/src/modules/doc/xmlContent/xmlContent.html +29 -17
  73. package/src/modules/doc/xmlContent/xmlContent.ts +212 -79
  74. package/src/modules/docHelpers/amfStyle/amfStyle.css +6 -6
  75. package/src/modules/docHelpers/contentLayoutStyle/contentLayoutStyle.css +131 -0
  76. package/src/modules/docHelpers/imgStyle/imgStyle.css +59 -0
  77. package/src/modules/docHelpers/status/status.css +1 -1
  78. package/src/modules/docUtils/{SearchSyncer/SearchSyncer.ts → searchSyncer/searchSyncer.ts} +1 -0
  79. package/src/modules/docUtils/utils/__mocks__/coveo.analytics.ts +16 -0
  80. package/src/modules/docUtils/utils/coveo.analytics.d.ts +10 -0
  81. package/src/modules/docUtils/utils/utils.ts +32 -0
  82. package/LICENSE +0 -12
  83. package/src/modules/docBaseElements/lightningElementWithState/lightningElementWithState.ts +0 -93
  84. package/src/modules/docHelpers/phaseContentLayout/phaseContentLayout.css +0 -39
@@ -1,12 +1,17 @@
1
+ /* eslint-disable @lwc/lwc/no-document-query */
1
2
  import { LightningElement, api, track } from "lwc";
2
3
  import { closest } from "kagekiri";
3
4
  import { toJson } from "dxUtils/normalizers";
4
5
  import { highlightTerms } from "dxUtils/highlight";
5
- import { SearchSyncer } from "docUtils/SearchSyncer";
6
+ import { SearchSyncer } from "docUtils/searchSyncer";
7
+ import type { OptionWithLink } from "typings/custom";
6
8
 
7
9
  type AnchorMap = { [key: string]: { intersect: boolean; id: string } };
8
10
 
9
- const TOC_HEADER_TAG = "DOC-HEADING";
11
+ declare const Sprig: (eventType: string, eventNme: string) => void;
12
+
13
+ const TOC_HEADER_TAG = "doc-heading";
14
+
10
15
  const HIGHLIGHTABLE_SELECTOR = [
11
16
  "p",
12
17
  "h1",
@@ -20,25 +25,35 @@ const HIGHLIGHTABLE_SELECTOR = [
20
25
  "th",
21
26
  "td"
22
27
  ].join(",");
23
- const OBSERVER_ATTACH_WAIT_TIME = 500;
28
+ export const OBSERVER_ATTACH_WAIT_TIME = 500;
24
29
 
25
30
  export default class ContentLayout extends LightningElement {
26
- @api sidebarValue: string;
27
- @api sidebarHeader: string;
28
- @api tocTitle: string;
31
+ @api sidebarValue!: string;
32
+ @api sidebarHeader!: string;
33
+ @api tocTitle!: string;
29
34
  @api enableSlotChange = false;
30
35
  @api coveoOrganizationId!: string;
31
36
  @api coveoPublicAccessToken!: string;
37
+ @api coveoAnalyticsToken!: string;
32
38
  @api coveoSearchHub!: string;
33
39
  @api coveoAdvancedQueryConfig!: string;
34
40
  @api useOldSidebar?: boolean = false;
41
+ @api languages!: OptionWithLink[];
42
+ @api language!: string;
43
+ @api bailHref!: string;
44
+ @api bailLabel!: string;
45
+ @api devCenter: any;
46
+ @api brand: any;
47
+
48
+ // This is needed for now to prevent failing snapshot tests due to links in the footer
49
+ @api showFooter = false;
35
50
 
36
51
  @api
37
52
  get breadcrumbs() {
38
53
  return this._breadcrumbs;
39
54
  }
40
55
 
41
- set breadcrumbs(value): [] {
56
+ set breadcrumbs(value) {
42
57
  if (value) {
43
58
  this._breadcrumbs = toJson(value);
44
59
  }
@@ -49,7 +64,7 @@ export default class ContentLayout extends LightningElement {
49
64
  return this._sidebarContent;
50
65
  }
51
66
 
52
- set sidebarContent(value) {
67
+ set sidebarContent(value: any) {
53
68
  this._sidebarContent = toJson(value);
54
69
  }
55
70
 
@@ -64,27 +79,33 @@ export default class ContentLayout extends LightningElement {
64
79
 
65
80
  @api
66
81
  setSidebarInputValue(searchTerm: string): void {
67
- this.template.querySelector("dx-sidebar")?.setInputValue(searchTerm);
82
+ (this.template.querySelector("dx-sidebar") as any)?.setInputValue(
83
+ searchTerm
84
+ );
68
85
  }
69
86
 
70
87
  @track
71
- private _sidebarContent: unknown;
88
+ protected _sidebarContent: unknown;
72
89
 
73
- private _breadcrumbs = null;
90
+ protected _breadcrumbs = null;
74
91
 
75
92
  @track
76
- private _tocOptions: Array<unknown>;
77
-
78
- private anchoredElements: AnchorMap = {};
79
- private lastScrollPosition: number;
80
- private observer?: IntersectionObserver;
81
- private hasRendered: boolean = false;
93
+ protected _tocOptions!: Array<unknown>;
94
+
95
+ protected tocOptionIdsSet = new Set();
96
+ protected anchoredElements: AnchorMap = {};
97
+ protected lastScrollPosition!: number;
98
+ protected observer?: IntersectionObserver;
99
+ protected hasRendered: boolean = false;
100
+ protected contentLoaded: boolean = false;
101
+ protected sidebarOpen: boolean = false;
102
+
103
+ get shouldDisplayFeedback() {
104
+ return this.contentLoaded && typeof Sprig !== "undefined";
105
+ }
82
106
 
83
- private searchSyncer = new SearchSyncer({
107
+ protected searchSyncer = new SearchSyncer({
84
108
  callbacks: {
85
- onUrlChange: (nextSearchString: string): void => {
86
- this.updateHighlightsAndSearch(nextSearchString);
87
- },
88
109
  onSearchChange: (nextSearchString: string): void => {
89
110
  this.dispatchHighlightChange(
90
111
  new URLSearchParams(nextSearchString).get("q") || ""
@@ -97,10 +118,11 @@ export default class ContentLayout extends LightningElement {
97
118
  shouldStopPropagation: true,
98
119
  target: window
99
120
  });
100
- private tocValue?: string = undefined;
101
- private observerTimerId = null;
102
- private didScrollToSelectedHash = false;
103
- private _scrollInterval = 0;
121
+ protected tocValue?: string = undefined;
122
+ // eslint-disable-next-line no-undef
123
+ protected observerTimerId?: NodeJS.Timeout;
124
+ protected didScrollToSelectedHash = false;
125
+ protected _scrollInterval = 0;
104
126
 
105
127
  get showToc(): boolean {
106
128
  return this.tocOptions && this.tocOptions.length > 0;
@@ -117,10 +139,6 @@ export default class ContentLayout extends LightningElement {
117
139
  );
118
140
  }
119
141
 
120
- get docContentStyle(): string {
121
- return this.showBreadcrumbs ? "" : "margin-top: 48px";
122
- }
123
-
124
142
  connectedCallback(): void {
125
143
  const hasParentHighlightListener = closest(
126
144
  "doc-xml-content",
@@ -133,12 +151,11 @@ export default class ContentLayout extends LightningElement {
133
151
  );
134
152
  this.searchSyncer.init();
135
153
  }
136
-
137
- this._scrollInterval = window.setInterval(() => {
138
- this.saveScroll();
139
- }, 1000);
140
154
  }
141
155
 
156
+ // Placeholder for childs renderedCallback
157
+ protected postRenderedCallback?(): void;
158
+
142
159
  renderedCallback(): void {
143
160
  /**
144
161
  * Note: We are adding timeout because chrome is optimizing and not triggering recent renderedCallback though elements reference is changed
@@ -149,9 +166,17 @@ export default class ContentLayout extends LightningElement {
149
166
  this.attachInteractionObserver,
150
167
  OBSERVER_ATTACH_WAIT_TIME
151
168
  );
169
+
170
+ this.adjustNavPosition();
171
+ window.addEventListener("scroll", this.adjustNavPosition);
172
+ window.addEventListener("resize", this.adjustNavPosition);
173
+
152
174
  if (!this.hasRendered) {
153
175
  this.hasRendered = true;
154
176
  this.restoreScroll();
177
+
178
+ // Dynamically call `renderedCallbackForLwcContentLayout` if it exists
179
+ this.postRenderedCallback?.();
155
180
  }
156
181
  }
157
182
 
@@ -161,20 +186,14 @@ export default class ContentLayout extends LightningElement {
161
186
  "highlightedtermchange",
162
187
  this.updateHighlighted
163
188
  );
189
+ window.removeEventListener("scroll", this.adjustNavPosition);
190
+ window.removeEventListener("resize", this.adjustNavPosition);
164
191
  this.searchSyncer.dispose();
165
192
  this.clearRenderObserverTimer();
166
193
 
167
194
  window.clearInterval(this._scrollInterval);
168
195
  }
169
196
 
170
- saveScroll() {
171
- window.history.replaceState(
172
- { scrollValue: document.body.scrollTop },
173
- "",
174
- window.location.href
175
- );
176
- }
177
-
178
197
  restoreScroll() {
179
198
  document.body.scrollTop = document.documentElement.scrollTop =
180
199
  window.history.state?.scrollValue;
@@ -186,98 +205,274 @@ export default class ContentLayout extends LightningElement {
186
205
  }
187
206
  };
188
207
 
189
- updateHighlighted = (event: Event): void =>
190
- highlightTerms(
191
- this.querySelectorAll(HIGHLIGHTABLE_SELECTOR),
192
- (event as CustomEvent<string>).detail
193
- );
208
+ /*
209
+ This is a workaround for the global nav sticky header being decoupled from the doc header & doc phase.
210
+ We have to account for the global nav changing height due to animations.
211
+ */
212
+ adjustNavPosition = () => {
213
+ const sidebarType = this.useOldSidebar
214
+ ? "dx-sidebar-old"
215
+ : "dx-sidebar";
216
+ const sidebarEl = this.template.querySelector(sidebarType);
217
+ const globalNavEl = document.querySelector(
218
+ "hgf-c360nav"
219
+ ) as HTMLElement;
220
+ const contextNavEl = document.querySelector(
221
+ "hgf-c360contextnav"
222
+ ) as HTMLElement;
223
+ const docHeaderEl = document.querySelector(
224
+ ".sticky-doc-header"
225
+ ) as HTMLElement;
226
+
227
+ let docPhaseEl = (
228
+ this.template.querySelector("[name=doc-phase]")! as any
229
+ ).assignedElements()[0] as HTMLSlotElement;
230
+
231
+ if (!docPhaseEl) {
232
+ docPhaseEl = (
233
+ this.template.querySelector("[name=version-banner]")! as any
234
+ ).assignedElements()[0] as HTMLSlotElement;
235
+ }
194
236
 
195
- attachInteractionObserver = (): void => {
196
- if (!this.enableSlotChange) {
237
+ if (!sidebarEl || !globalNavEl || !contextNavEl || !docHeaderEl) {
238
+ console.warn("One or more required elements are missing.");
197
239
  return;
198
240
  }
199
- this.disconnectObserver();
200
- this.observer = new IntersectionObserver((entries) => {
201
- entries.forEach(
202
- (entry) =>
203
- (this.anchoredElements[
204
- entry.target.getAttribute("id")
205
- ].intersect = entry.isIntersecting)
241
+
242
+ // sync with the browser to account for any reflows that may have happened
243
+ requestAnimationFrame(() => {
244
+ // ternary is a temporary fix for the global nav height reporting incorrectly on some browsers
245
+ const globalNavHeight =
246
+ (globalNavEl.getBoundingClientRect().height !== 72 ? 0 : 72) +
247
+ contextNavEl.getBoundingClientRect().height;
248
+ const docHeaderHeight = docHeaderEl.getBoundingClientRect().height;
249
+ const totalHeaderHeight = globalNavHeight + docHeaderHeight;
250
+
251
+ // Selecting the doc section heading and RNB here.
252
+ const docHeadingEls = Array.from(
253
+ document.querySelectorAll("doc-heading")
206
254
  );
207
- this.calculateActualSection();
255
+ const rightNavBarEl = this.template.querySelector(".right-nav-bar");
256
+
257
+ sidebarEl.style.setProperty(
258
+ "--dx-c-content-sidebar-sticky-top",
259
+ `${globalNavHeight + docHeaderHeight}px`
260
+ );
261
+
262
+ docHeaderEl.style.setProperty(
263
+ "--dx-g-global-header-height",
264
+ `${globalNavHeight}px`
265
+ );
266
+
267
+ // Adjusting the doc section heading on scroll.
268
+ docHeadingEls.forEach((docHeadingEl) => {
269
+ (docHeadingEl as any).style.scrollMarginTop = docPhaseEl
270
+ ? `${
271
+ totalHeaderHeight +
272
+ docPhaseEl.getBoundingClientRect().height +
273
+ 40
274
+ }px`
275
+ : `${totalHeaderHeight + 40}px`;
276
+ });
277
+
278
+ // Adjusting the right nav bar on scroll.
279
+ if (rightNavBarEl) {
280
+ rightNavBarEl.style.top = docPhaseEl
281
+ ? `${
282
+ totalHeaderHeight +
283
+ docPhaseEl.getBoundingClientRect().height
284
+ }px`
285
+ : `${totalHeaderHeight}px`;
286
+ }
287
+
288
+ // If doc phase element exists, we need to account for its sticky position. Mobile should include the sidebar height (since it becomes sticky aswell).
289
+ if (docPhaseEl) {
290
+ docPhaseEl.style.setProperty(
291
+ "--doc-c-phase-top",
292
+ `${
293
+ window.innerWidth < 769
294
+ ? globalNavHeight +
295
+ docHeaderHeight +
296
+ sidebarEl.getBoundingClientRect().height
297
+ : globalNavHeight + docHeaderHeight
298
+ }px`
299
+ );
300
+ }
208
301
  });
302
+ };
303
+
304
+ updateHighlighted = (event: Event): void =>
305
+ highlightTerms(
306
+ this.querySelectorAll(HIGHLIGHTABLE_SELECTOR),
307
+ (event as CustomEvent<string>).detail
308
+ );
209
309
 
310
+ protected getHeadingElements() {
210
311
  // Note: We are doing document.querySelectorAll as a quick fix as we are not getting heading elements reference this.querySelectorAll
211
312
  const headingElements = document.querySelectorAll(TOC_HEADER_TAG);
212
- for (const headingElement of headingElements) {
313
+ return headingElements;
314
+ }
315
+
316
+ updateHeadingForRNB(): void {
317
+ const headingElements = this.getHeadingElements();
318
+ this.addObserverAndScroll(headingElements);
319
+ }
320
+
321
+ addObserverAndScroll(headingElements: any) {
322
+ for (const headingElement of headingElements as any) {
213
323
  // Add headingElements to intersectionObserver for highlighting respective RNB item when user scroll
214
- const id = headingElement.getAttribute("id");
324
+ const id = headingElement.getAttribute("id")!;
215
325
  this.anchoredElements[id] = {
216
326
  id,
217
327
  intersect: false
218
328
  };
219
- this.observer.observe(headingElement);
329
+ this.observer?.observe(headingElement);
220
330
  }
331
+
221
332
  if (!this.didScrollToSelectedHash) {
222
333
  this.didScrollToSelectedHash = true;
223
334
  this.scrollToHash(headingElements);
224
335
  }
225
- };
336
+ }
226
337
 
227
- onSlotChange(event: Event): void {
228
- const slotElements = (
229
- event.target as HTMLSlotElement
230
- ).assignedElements();
338
+ attachInteractionObserver = (): void => {
339
+ if (!this.enableSlotChange) {
340
+ return;
341
+ }
342
+ this.disconnectObserver();
231
343
 
232
- if (slotElements.length) {
233
- const slotContentElement = slotElements[0];
234
- const headingElements =
235
- slotContentElement.ownerDocument?.getElementsByTagName(
236
- TOC_HEADER_TAG
344
+ const globalNavOffset = `-${getComputedStyle(
345
+ document.documentElement
346
+ ).getPropertyValue("--dx-g-doc-header-main-nav-height")}`;
347
+
348
+ this.observer = new IntersectionObserver(
349
+ (entries) => {
350
+ entries.forEach(
351
+ (entry) =>
352
+ (this.anchoredElements[
353
+ entry.target.getAttribute("id")!
354
+ ].intersect = entry.isIntersecting)
237
355
  );
238
- for (const headingElement of headingElements) {
239
- // Sometimes elements hash is not being set when slot content is wrapped with div
240
- headingElement.hash = headingElement.attributes.hash?.nodeValue;
356
+ this.calculateActualSection();
357
+ },
358
+ {
359
+ rootMargin: globalNavOffset.trim()
241
360
  }
242
- const tocOptions = [];
243
- for (const headingElement of headingElements) {
244
- headingElement.id = headingElement.hash;
361
+ );
362
+ this.updateHeadingForRNB();
363
+ };
245
364
 
246
- // Update tocOptions from anchorTags
365
+ // eslint-disable-next-line no-undef
366
+ updateTocItems(headingElements: any): void {
367
+ const tocOptions = [];
368
+
369
+ for (const headingElement of headingElements as any) {
370
+ headingElement.id = headingElement.hash;
371
+
372
+ // Update tocOptions from anchorTags only for H2, consider default as 2 as per component
373
+ const headingAriaLevel =
374
+ headingElement.attributes["aria-level"]?.nodeValue || "2";
375
+ const isH2 = headingAriaLevel === "2";
376
+
377
+ if (isH2) {
247
378
  const tocItem = {
248
379
  anchor: `#${headingElement.hash}`,
249
380
  id: headingElement.id,
250
- label: headingElement.title
381
+ label: headingElement.header
251
382
  };
252
383
  tocOptions.push(tocItem);
384
+ this.tocOptionIdsSet.add(headingElement.id);
253
385
  }
386
+ }
254
387
 
255
- this._tocOptions = tocOptions;
388
+ this._tocOptions = tocOptions;
389
+ }
390
+
391
+ setHashAndHeaderForDocHeading(headingElements: any) {
392
+ for (const headingElement of headingElements as any) {
393
+ // Sometimes elements hash and header is not being set when slot content is wrapped with div
394
+ if (!headingElement.hash) {
395
+ headingElement.hash = headingElement.attributes.hash?.nodeValue;
396
+ }
397
+
398
+ if (!headingElement.header) {
399
+ headingElement.header =
400
+ headingElement.attributes.header?.nodeValue;
401
+ }
256
402
  }
403
+
404
+ this.updateTocItems(headingElements);
405
+ }
406
+
407
+ updateRNB = () => {
408
+ const headingElements = this.getHeadingElements();
409
+ this.setHashAndHeaderForDocHeading(headingElements);
410
+ };
411
+
412
+ onSlotChange(): void {
413
+ this.updateRNB();
414
+ this.contentLoaded = true;
257
415
  }
258
416
 
259
- private disconnectObserver(): void {
417
+ protected disconnectObserver(): void {
260
418
  if (this.observer) {
261
419
  this.observer.disconnect();
262
- this.observer = null;
420
+ this.observer = undefined;
263
421
  }
264
422
  }
265
423
 
266
424
  // eslint-disable-next-line no-undef
267
- private scrollToHash(headingElements: NodeListOf<Element>): void {
425
+ protected scrollToHash(headingElements: NodeListOf<Element>): void {
268
426
  let { hash } = window.location;
269
427
  if (hash) {
270
428
  hash = hash.substr(1);
271
- for (const headingElement of headingElements) {
429
+
430
+ const docHeaderEl = document.querySelector(
431
+ ".sticky-doc-header"
432
+ ) as HTMLElement;
433
+ const globalNavEl = document.querySelector(
434
+ "hgf-c360nav"
435
+ ) as HTMLElement;
436
+ const contextNavEl = document.querySelector(
437
+ "hgf-c360contextnav"
438
+ ) as HTMLElement;
439
+
440
+ const headerHeight =
441
+ docHeaderEl?.offsetHeight +
442
+ globalNavEl?.offsetHeight +
443
+ contextNavEl?.offsetHeight;
444
+
445
+ const docPhaseEl = (
446
+ this.template.querySelector("[name=doc-phase]")! as any
447
+ ).assignedElements()[0] as HTMLSlotElement;
448
+
449
+ const offset = docPhaseEl
450
+ ? headerHeight + docPhaseEl.offsetHeight
451
+ : headerHeight;
452
+
453
+ for (const headingElement of headingElements as any) {
272
454
  if (headingElement.getAttribute("id") === hash) {
273
- headingElement.scrollIntoView({ behavior: "auto" });
455
+ this.scrollIntoViewWithOffset(headingElement, offset);
274
456
  break;
275
457
  }
276
458
  }
277
459
  }
278
460
  }
279
461
 
280
- private calculateActualSection(): void {
462
+ protected scrollIntoViewWithOffset(
463
+ headingElement: HTMLElement,
464
+ offset: number
465
+ ) {
466
+ window.scrollTo({
467
+ behavior: "auto",
468
+ top:
469
+ headingElement.getBoundingClientRect().top -
470
+ document.body.getBoundingClientRect().top -
471
+ offset
472
+ });
473
+ }
474
+
475
+ protected calculateActualSection(): void {
281
476
  const currentScrollPosition = document.documentElement.scrollTop;
282
477
  const id = Object.keys(this.anchoredElements).find(
283
478
  (_id) => this.anchoredElements[_id].intersect
@@ -292,18 +487,21 @@ export default class ContentLayout extends LightningElement {
292
487
  this.lastScrollPosition = currentScrollPosition;
293
488
  }
294
489
 
295
- private calculatePreviousElementId(): string {
490
+ protected calculatePreviousElementId(): string | undefined {
296
491
  const keys = Object.keys(this.anchoredElements);
297
492
  const currentIndex = keys.findIndex((id) => this.tocValue === id);
298
493
 
299
494
  return currentIndex > 0 ? keys[currentIndex - 1] : undefined;
300
495
  }
301
496
 
302
- private assignElementId(id: string): void {
303
- this.tocValue = id;
497
+ protected assignElementId(id: string | undefined): void {
498
+ // Change toc(RNB) highlight only for H2
499
+ if (this.tocOptionIdsSet.has(id)) {
500
+ this.tocValue = id;
501
+ }
304
502
  }
305
503
 
306
- private dispatchHighlightChange(term: string): void {
504
+ protected dispatchHighlightChange(term: string): void {
307
505
  this.dispatchEvent(
308
506
  new CustomEvent("highlightedtermchange", {
309
507
  detail: term,
@@ -313,10 +511,21 @@ export default class ContentLayout extends LightningElement {
313
511
  );
314
512
  }
315
513
 
316
- private updateHighlightsAndSearch(nextSearchString: string): void {
514
+ protected updateHighlightsAndSearch(nextSearchString: string): void {
317
515
  const nextSearchParam =
318
516
  new URLSearchParams(nextSearchString).get("q") || "";
319
517
  this.setSidebarInputValue(nextSearchParam);
320
518
  this.dispatchHighlightChange(nextSearchParam);
321
519
  }
520
+
521
+ protected onToggleSidebar(e: CustomEvent): void {
522
+ this.sidebarOpen = e.detail.open;
523
+
524
+ // eslint-disable-next-line @lwc/lwc/no-document-query
525
+ const footer = document.querySelector("dx-footer") as HTMLElement;
526
+
527
+ if (footer) {
528
+ footer.style.display = this.sidebarOpen ? "none" : "block";
529
+ }
530
+ }
322
531
  }
@@ -0,0 +1,47 @@
1
+ @import "dxHelpers/reset";
2
+ @import "dxHelpers/text";
3
+
4
+ .container {
5
+ display: flex;
6
+ flex-direction: column;
7
+ gap: var(--dx-g-spacing-md);
8
+ flex: 1;
9
+ }
10
+
11
+ .doc-do-dont-header {
12
+ display: flex;
13
+ align-items: center;
14
+ gap: var(--dx-g-spacing-sm);
15
+ }
16
+
17
+ .doc-do-dont-label {
18
+ font-family: var(--dx-g-font-display);
19
+ font-size: var(--dx-g-spacing-md);
20
+ font-weight: var(--dx-g-font-demi);
21
+ line-height: var(--dx-g-spacing-lg);
22
+ }
23
+
24
+ .doc-do-color {
25
+ color: var(--dx-g-green-vibrant-50);
26
+ }
27
+
28
+ .doc-dont-color {
29
+ color: var(--dx-g-red-vibrant-30);
30
+ }
31
+
32
+ .do-dont-image-container {
33
+ display: flex;
34
+ max-height: 480px;
35
+ min-height: 140px;
36
+ padding: var(--dx-g-spacing-3xl) var(--dx-g-spacing-2xl);
37
+ flex-direction: column;
38
+ justify-content: center;
39
+ align-items: center;
40
+ flex: 1;
41
+ border-radius: var(--dx-g-spacing-sm);
42
+ border: 1px solid var(--dx-g-brand-default-color-border-2);
43
+ }
44
+
45
+ .doc-do-dont-img {
46
+ object-fit: contain;
47
+ }
@@ -0,0 +1,27 @@
1
+ <template>
2
+ <div class="container">
3
+ <div class="doc-do-dont-header">
4
+ <template lwc:if={isDo}>
5
+ <dx-icon
6
+ symbol="success"
7
+ size="large"
8
+ color="green-vibrant-50"
9
+ ></dx-icon>
10
+ <div class="doc-do-dont-label doc-do-color">Do</div>
11
+ </template>
12
+ <template lwc:else>
13
+ <dx-icon
14
+ symbol="clear"
15
+ size="large"
16
+ color="red-vibrant-30"
17
+ class="doc-do-dont-icon"
18
+ ></dx-icon>
19
+ <div class="doc-do-dont-label doc-dont-color">Don't</div>
20
+ </template>
21
+ </div>
22
+ <div class="do-dont-image-container">
23
+ <img class="doc-do-dont-img" src={imgSrc} alt={caption} />
24
+ </div>
25
+ <div class="dx-text-body-4">{caption}</div>
26
+ </div>
27
+ </template>
@@ -0,0 +1,17 @@
1
+ import { LightningElement, api } from "lwc";
2
+ import { normalizeBoolean } from "dxUtils/normalizers";
3
+
4
+ export default class DoDont extends LightningElement {
5
+ @api caption: string = "";
6
+ @api imgSrc!: string;
7
+ _isDo: boolean = false;
8
+
9
+ @api
10
+ get isDo(): boolean {
11
+ return this._isDo;
12
+ }
13
+
14
+ set isDo(value) {
15
+ this._isDo = normalizeBoolean(value);
16
+ }
17
+ }