@salesforcedevs/docs-components 1.3.2 → 1.3.4-alpha

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