@salesforcedevs/docs-components 0.0.0 → 0.0.1-superscript

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