@salesforcedevs/docs-components 0.0.1-edit → 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 (41) hide show
  1. package/lwc.config.json +2 -1
  2. package/package.json +29 -28
  3. package/src/modules/doc/amfReference/amfReference.css +0 -12
  4. package/src/modules/doc/amfReference/amfReference.html +2 -6
  5. package/src/modules/doc/amfReference/amfReference.ts +48 -37
  6. package/src/modules/doc/amfTopic/amfTopic.ts +24 -0
  7. package/src/modules/doc/breadcrumbs/breadcrumbs.html +0 -1
  8. package/src/modules/doc/componentPlayground/componentPlayground.css +11 -3
  9. package/src/modules/doc/componentPlayground/componentPlayground.html +4 -4
  10. package/src/modules/doc/componentPlayground/componentPlayground.ts +69 -1
  11. package/src/modules/doc/content/content.ts +0 -1
  12. package/src/modules/doc/contentCallout/contentCallout.css +1 -0
  13. package/src/modules/doc/contentLayout/contentLayout.html +53 -56
  14. package/src/modules/doc/contentLayout/contentLayout.ts +82 -43
  15. package/src/modules/doc/contentMedia/contentMedia.css +1 -1
  16. package/src/modules/doc/header/header.html +5 -1
  17. package/src/modules/doc/header/header.ts +10 -0
  18. package/src/modules/doc/lwcContentLayout/lwcContentLayout.css +8 -0
  19. package/src/modules/doc/lwcContentLayout/lwcContentLayout.html +38 -42
  20. package/src/modules/doc/lwcContentLayout/lwcContentLayout.ts +116 -15
  21. package/src/modules/doc/phase/phase.css +0 -7
  22. package/src/modules/doc/redocReference/redocReference.css +7 -0
  23. package/src/modules/doc/redocReference/redocReference.html +13 -0
  24. package/src/modules/doc/redocReference/redocReference.ts +425 -0
  25. package/src/modules/doc/specificationContent/specificationContent.html +15 -9
  26. package/src/modules/doc/specificationContent/specificationContent.ts +39 -0
  27. package/src/modules/doc/superscriptSubscript/superscriptSubscript.html +8 -0
  28. package/src/modules/doc/superscriptSubscript/superscriptSubscript.ts +16 -0
  29. package/src/modules/doc/versionPicker/versionPicker.html +2 -0
  30. package/src/modules/doc/xmlContent/xmlContent.css +0 -10
  31. package/src/modules/doc/xmlContent/xmlContent.html +11 -8
  32. package/src/modules/doc/xmlContent/xmlContent.ts +95 -53
  33. package/src/modules/docHelpers/amfStyle/amfStyle.css +0 -2
  34. package/src/modules/docHelpers/contentLayoutStyle/contentLayoutStyle.css +32 -1
  35. package/src/modules/doc/chat/README.md +0 -179
  36. package/src/modules/doc/chat/chat.css +0 -818
  37. package/src/modules/doc/chat/chat.html +0 -241
  38. package/src/modules/doc/chat/chat.ts +0 -586
  39. package/src/modules/doc/editFile/editFile.css +0 -514
  40. package/src/modules/doc/editFile/editFile.html +0 -164
  41. package/src/modules/doc/editFile/editFile.ts +0 -213
@@ -5,6 +5,7 @@ import { toJson } from "dxUtils/normalizers";
5
5
  import { highlightTerms } from "dxUtils/highlight";
6
6
  import { SearchSyncer } from "docUtils/searchSyncer";
7
7
  import type { OptionWithLink } from "typings/custom";
8
+ import { buildDocLinkClickHandler } from "dxUtils/analytics";
8
9
 
9
10
  type AnchorMap = { [key: string]: { intersect: boolean; id: string } };
10
11
 
@@ -32,11 +33,6 @@ export default class ContentLayout extends LightningElement {
32
33
  @api sidebarHeader!: string;
33
34
  @api tocTitle!: string;
34
35
  @api enableSlotChange = false;
35
- @api coveoOrganizationId!: string;
36
- @api coveoPublicAccessToken!: string;
37
- @api coveoAnalyticsToken!: string;
38
- @api coveoSearchHub!: string;
39
- @api coveoAdvancedQueryConfig!: string;
40
36
  @api useOldSidebar?: boolean = false;
41
37
  @api languages!: OptionWithLink[];
42
38
  @api language!: string;
@@ -44,10 +40,13 @@ export default class ContentLayout extends LightningElement {
44
40
  @api bailLabel!: string;
45
41
  @api devCenter: any;
46
42
  @api brand: any;
43
+ @api emptyStateMessage?: string;
47
44
 
48
45
  // This is needed for now to prevent failing snapshot tests due to links in the footer
49
46
  @api showFooter = false;
50
47
 
48
+ @api readingTime?: number;
49
+
51
50
  @api
52
51
  get breadcrumbs() {
53
52
  return this._breadcrumbs;
@@ -77,6 +76,12 @@ export default class ContentLayout extends LightningElement {
77
76
  this._tocOptions = toJson(value);
78
77
  }
79
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
+
80
85
  @api
81
86
  setSidebarInputValue(searchTerm: string): void {
82
87
  (this.template.querySelector("dx-sidebar") as any)?.setInputValue(
@@ -123,6 +128,7 @@ export default class ContentLayout extends LightningElement {
123
128
  protected observerTimerId?: NodeJS.Timeout;
124
129
  protected didScrollToSelectedHash = false;
125
130
  protected _scrollInterval = 0;
131
+ protected handleLinkClick = buildDocLinkClickHandler();
126
132
 
127
133
  get showToc(): boolean {
128
134
  return this.tocOptions && this.tocOptions.length > 0;
@@ -139,6 +145,10 @@ export default class ContentLayout extends LightningElement {
139
145
  );
140
146
  }
141
147
 
148
+ get showReadingTime(): boolean {
149
+ return this.readingTime != null && this.readingTime > 1;
150
+ }
151
+
142
152
  connectedCallback(): void {
143
153
  const hasParentHighlightListener = closest(
144
154
  "doc-xml-content",
@@ -151,6 +161,9 @@ export default class ContentLayout extends LightningElement {
151
161
  );
152
162
  this.searchSyncer.init();
153
163
  }
164
+
165
+ // Add click handler for all links
166
+ this.template.addEventListener("click", this.handleLinkClick);
154
167
  }
155
168
 
156
169
  // Placeholder for childs renderedCallback
@@ -192,6 +205,9 @@ export default class ContentLayout extends LightningElement {
192
205
  this.clearRenderObserverTimer();
193
206
 
194
207
  window.clearInterval(this._scrollInterval);
208
+
209
+ // Remove link click handler
210
+ this.template.removeEventListener("click", this.handleLinkClick);
195
211
  }
196
212
 
197
213
  restoreScroll() {
@@ -210,10 +226,7 @@ export default class ContentLayout extends LightningElement {
210
226
  We have to account for the global nav changing height due to animations.
211
227
  */
212
228
  adjustNavPosition = () => {
213
- const sidebarType = this.useOldSidebar
214
- ? "dx-sidebar-old"
215
- : "dx-sidebar";
216
- const sidebarEl = this.template.querySelector(sidebarType);
229
+ const sidebarEl = this.template.querySelector("dx-sidebar-old");
217
230
  const globalNavEl = document.querySelector(
218
231
  "hgf-c360nav"
219
232
  ) as HTMLElement;
@@ -224,15 +237,20 @@ export default class ContentLayout extends LightningElement {
224
237
  ".sticky-doc-header"
225
238
  ) as HTMLElement;
226
239
 
227
- let docPhaseEl = (
228
- this.template.querySelector("[name=doc-phase]")! as any
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
229
249
  ).assignedElements()[0] as HTMLSlotElement;
230
250
 
231
- if (!docPhaseEl) {
232
- docPhaseEl = (
233
- this.template.querySelector("[name=version-banner]")! as any
234
- ).assignedElements()[0] as HTMLSlotElement;
235
- }
251
+ const verBannerEl = (
252
+ versionWrapper.querySelector("[name=version-banner]")! as any
253
+ ).assignedElements()[0] as HTMLSlotElement;
236
254
 
237
255
  if (!sidebarEl || !globalNavEl || !contextNavEl || !docHeaderEl) {
238
256
  console.warn("One or more required elements are missing.");
@@ -264,39 +282,54 @@ export default class ContentLayout extends LightningElement {
264
282
  `${globalNavHeight}px`
265
283
  );
266
284
 
285
+ const docPhaseElHeight =
286
+ docPhaseEl || verBannerEl
287
+ ? (docPhaseEl || verBannerEl).getBoundingClientRect().height
288
+ : 0;
289
+
267
290
  // Adjusting the doc section heading on scroll.
268
291
  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`;
292
+ (docHeadingEl as any).style.scrollMarginTop = `${
293
+ totalHeaderHeight + docPhaseElHeight + 40
294
+ }px`;
276
295
  });
277
296
 
278
297
  // Adjusting the right nav bar on scroll.
298
+ // setting maxheight to make the RNB scrollable based on different parent elements
279
299
  if (rightNavBarEl) {
280
- rightNavBarEl.style.top = docPhaseEl
281
- ? `${
282
- totalHeaderHeight +
283
- docPhaseEl.getBoundingClientRect().height
284
- }px`
285
- : `${totalHeaderHeight}px`;
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
+ }
286
317
  }
287
318
 
319
+ const docPhaseTop =
320
+ window.innerWidth < 769
321
+ ? globalNavHeight +
322
+ docHeaderHeight +
323
+ sidebarEl.getBoundingClientRect().height
324
+ : globalNavHeight + docHeaderHeight;
325
+
288
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
289
328
  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
- );
329
+ docPhaseWrapper.style.top = `${docPhaseTop}px`;
330
+ } else if (verBannerEl) {
331
+ versionWrapper.style.position = "sticky";
332
+ versionWrapper.style.top = `${docPhaseTop}px`;
300
333
  }
301
334
  });
302
335
  };
@@ -366,19 +399,25 @@ export default class ContentLayout extends LightningElement {
366
399
  updateTocItems(headingElements: any): void {
367
400
  const tocOptions = [];
368
401
 
402
+ const maxLevel = Number.parseInt(this.tocAriaLevel, 10) || 2;
403
+ const minLevel = 2;
404
+
369
405
  for (const headingElement of headingElements as any) {
370
406
  headingElement.id = headingElement.hash;
371
407
 
372
- // Update tocOptions from anchorTags only for H2, consider default as 2 as per component
408
+ // Update tocOptions from anchorTags for aria-levels 2..tocAriaLevel (inclusive)
373
409
  const headingAriaLevel =
374
410
  headingElement.attributes["aria-level"]?.nodeValue || "2";
375
- const isH2 = headingAriaLevel === "2";
411
+ const headingLevel = Number.parseInt(headingAriaLevel, 10) || 2;
412
+ const matchesLevel =
413
+ headingLevel >= minLevel && headingLevel <= maxLevel;
376
414
 
377
- if (isH2) {
415
+ if (matchesLevel) {
378
416
  const tocItem = {
379
417
  anchor: `#${headingElement.hash}`,
380
418
  id: headingElement.id,
381
- label: headingElement.header
419
+ label: headingElement.header,
420
+ level: headingLevel
382
421
  };
383
422
  tocOptions.push(tocItem);
384
423
  this.tocOptionIdsSet.add(headingElement.id);
@@ -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
 
@@ -8,7 +8,11 @@
8
8
  ></dx-banner>
9
9
  <!-- To-Do: Move the devCenter as a new component and use it here, as devCenter is also used in Sidebar now -->
10
10
  <div lwc:if={devCenter} class="dev-center-link">
11
- <a href={devCenter.link} class="dev-center-content">
11
+ <a
12
+ href={devCenter.link}
13
+ onclick={onLinkClick}
14
+ class="dev-center-content"
15
+ >
12
16
  <dx-icon symbol="back"></dx-icon>
13
17
  <dx-icon
14
18
  class="brand-icon"
@@ -3,6 +3,7 @@ import cx from "classnames";
3
3
  import type { OptionWithNested, DevCenterConfig } from "typings/custom";
4
4
  import { HeaderBase } from "dxBaseElements/headerBase";
5
5
  import { toJson, normalizeBoolean } from "dxUtils/normalizers";
6
+ import { track } from "dxUtils/analytics";
6
7
 
7
8
  const TABLET_MATCH = "980px";
8
9
  const MOBILE_MATCH = "768px";
@@ -117,4 +118,13 @@ export default class Header extends HeaderBase {
117
118
  this.showScopedNavItems && "has-scoped-nav-items"
118
119
  );
119
120
  }
121
+
122
+ private onLinkClick(event: Event): void {
123
+ track(event.target!, "custEv_linkClick", {
124
+ click_text: this.devCenter.title,
125
+ click_url: this.devCenter.link,
126
+ element_type: "link",
127
+ content_category: "dev-center"
128
+ });
129
+ }
120
130
  }
@@ -1 +1,9 @@
1
1
  @import "docHelpers/contentLayoutStyle";
2
+
3
+ .fixed-right-nav-bar {
4
+ width: 180px;
5
+ }
6
+
7
+ .content-body-no-rnb {
8
+ max-width: 1220px;
9
+ }
@@ -1,58 +1,54 @@
1
1
  <template>
2
2
  <div class="content">
3
- <template lwc:if={useOldSidebar}>
4
- <dx-sidebar-old
5
- class="is-sticky left-nav-bar"
6
- trees={sidebarContent}
7
- value={sidebarValue}
8
- header={sidebarHeader}
9
- ontogglesidebar={onToggleSidebar}
10
- languages={languages}
11
- language={language}
12
- bail-href={bailHref}
13
- bail-label={bailLabel}
14
- dev-center={devCenter}
15
- brand={brand}
16
- >
17
- <slot name="sidebar-header" slot="version-picker"></slot>
18
- </dx-sidebar-old>
19
- </template>
20
- <template lwc:else>
21
- <dx-sidebar
22
- class="is-sticky left-nav-bar"
23
- trees={sidebarContent}
24
- value={sidebarValue}
25
- header={sidebarHeader}
26
- coveo-organization-id={coveoOrganizationId}
27
- coveo-public-access-token={coveoPublicAccessToken}
28
- coveo-search-hub={coveoSearchHub}
29
- coveo-advanced-query-config={coveoAdvancedQueryConfig}
30
- ontogglesidebar={onToggleSidebar}
31
- languages={languages}
32
- language={language}
33
- bail-href={bailHref}
34
- bail-label={bailLabel}
35
- dev-center={devCenter}
36
- brand={brand}
37
- >
38
- <slot name="sidebar-header" slot="version-picker"></slot>
39
- </dx-sidebar>
40
- </template>
3
+ <dx-sidebar-old
4
+ class="is-sticky left-nav-bar"
5
+ trees={sidebarContent}
6
+ value={sidebarValue}
7
+ header={sidebarHeader}
8
+ ontogglesidebar={onToggleSidebar}
9
+ languages={languages}
10
+ language={language}
11
+ bail-href={bailHref}
12
+ bail-label={bailLabel}
13
+ dev-center={devCenter}
14
+ brand={brand}
15
+ >
16
+ <slot name="sidebar-header" slot="version-picker"></slot>
17
+ </dx-sidebar-old>
41
18
  <div class="content-body-doc-phase-container">
42
- <slot name="doc-phase"></slot>
43
- <slot name="version-banner"></slot>
19
+ <div class="doc-phase-wrapper">
20
+ <slot name="doc-phase"></slot>
21
+ </div>
22
+ <div class="version-wrapper">
23
+ <slot name="version-banner"></slot>
24
+ </div>
44
25
  <div class="content-body-container">
45
- <div class="content-body">
26
+ <div class={contentBodyClasses}>
46
27
  <doc-breadcrumbs
47
28
  lwc:if={showBreadcrumbs}
48
29
  breadcrumbs={breadcrumbs}
49
30
  ></doc-breadcrumbs>
31
+ <div class="read" lwc:if={showReadingTime}>
32
+ <svg
33
+ xmlns="http://www.w3.org/2000/svg"
34
+ width="18"
35
+ height="18"
36
+ viewBox="0 0 52 52"
37
+ >
38
+ <g fill="#3e3e3c">
39
+ <path
40
+ d="m26 2c-13.2 0-24 10.8-24 24s10.8 24 24 24 24-10.8 24-24-10.8-24-24-24z m0 42c-9.9 0-18-8.1-18-18s8.1-18 18-18 18 8.1 18 18-8.1 18-18 18z m3.4-17.8c-0.3-0.3-0.4-0.7-0.4-1.1v-9.6c0-0.8-0.7-1.5-1.5-1.5h-3c-0.8 0-1.5 0.7-1.5 1.5v12.1c0 0.4 0.2 0.8 0.4 1.1l7.4 7.4c0.6 0.6 1.5 0.6 2.1 0l2.1-2.1c0.6-0.6 0.6-1.5 0-2.1l-5.6-5.7z"
41
+ ></path>
42
+ </g>
43
+ </svg>
44
+ {readingTime} minute read
45
+ </div>
50
46
  <slot onslotchange={onSlotChange}></slot>
51
47
  <doc-sprig-survey
52
48
  lwc:if={shouldDisplayFeedback}
53
49
  ></doc-sprig-survey>
54
50
  </div>
55
- <div lwc:if={showToc} class="right-nav-bar is-sticky">
51
+ <div lwc:if={showToc} class={rightNavBarClasses}>
56
52
  <dx-toc
57
53
  header={tocTitle}
58
54
  options={tocOptions}
@@ -1,45 +1,125 @@
1
1
  import ContentLayout from "doc/contentLayout";
2
+ import cx from "classnames";
2
3
 
3
4
  const TOC_HEADER_TAG = "doc-heading";
4
5
  const RNB_BY_TAB = "docs-tab";
5
- const SPECIFICATION_TAB_TITLE = "Specification";
6
+ const SPECIFICATION_TAG = "doc-specification-content";
6
7
  export const OBSERVER_ATTACH_WAIT_TIME = 500;
7
8
 
8
9
  export default class LwcContentLayout extends ContentLayout {
9
10
  private rnbByTab: boolean = false;
10
11
 
12
+ // DOM element caches to avoid repeated queries
13
+ private tabPanelListCache: any = null;
14
+ private hasSpecContentCache: boolean | null = null;
15
+ private allTabsCache: any[] | null = null;
16
+ private mainSlotCache: any = null;
17
+
11
18
  private setRNBByTab() {
12
- const tabPanelListItem: any = this.getTabPanelList();
13
- this.rnbByTab = tabPanelListItem?.id === RNB_BY_TAB ? true : false;
19
+ const tabPanelListItem = this.getTabPanelList();
20
+ this.rnbByTab = tabPanelListItem?.id === RNB_BY_TAB;
14
21
  }
15
22
 
16
23
  get showTabBasedRNB() {
17
24
  return this.rnbByTab;
18
25
  }
19
26
 
27
+ get rightNavBarClasses() {
28
+ return cx(
29
+ "right-nav-bar",
30
+ "is-sticky",
31
+ this.showTabBasedRNB && "fixed-right-nav-bar"
32
+ );
33
+ }
34
+
35
+ get contentBodyClasses() {
36
+ return cx("content-body", !this.showToc && "content-body-no-rnb");
37
+ }
38
+
39
+ /**
40
+ * Check if the main slot contains doc-specification-content
41
+ * Uses caching to avoid repeated DOM queries
42
+ */
43
+ private hasSpecificationContent(): boolean {
44
+ if (this.hasSpecContentCache !== null) {
45
+ return this.hasSpecContentCache;
46
+ }
47
+
48
+ const mainSlot = this.getMainSlot();
49
+ if (mainSlot) {
50
+ const assignedElements = (mainSlot as any).assignedElements();
51
+
52
+ for (const element of assignedElements) {
53
+ if (
54
+ element.tagName === SPECIFICATION_TAG ||
55
+ element.querySelector(SPECIFICATION_TAG) ||
56
+ element.shadowRoot?.querySelector(SPECIFICATION_TAG)
57
+ ) {
58
+ return (this.hasSpecContentCache = true);
59
+ }
60
+ }
61
+ }
62
+ return (this.hasSpecContentCache = false);
63
+ }
64
+
65
+ /**
66
+ * Clear all caches when content changes
67
+ */
68
+ private clearAllCaches(): void {
69
+ this.hasSpecContentCache = null;
70
+ this.tabPanelListCache = null;
71
+ this.allTabsCache = null;
72
+ this.mainSlotCache = null;
73
+ }
74
+
75
+ /**
76
+ * Get the main slot element with caching
77
+ */
78
+ private getMainSlot(): any {
79
+ if (!this.mainSlotCache) {
80
+ this.mainSlotCache =
81
+ this.template.querySelector("slot:not([name])");
82
+ }
83
+ return this.mainSlotCache;
84
+ }
85
+
86
+ /**
87
+ * Get tab panel list with caching
88
+ */
89
+ private getTabPanelList(): any {
90
+ if (!this.tabPanelListCache) {
91
+ // eslint-disable-next-line @lwc/lwc/no-document-query
92
+ this.tabPanelListCache =
93
+ document.querySelector("dx-tab-panel-list");
94
+ }
95
+ return this.tabPanelListCache;
96
+ }
97
+
20
98
  onRNBClick = (event: CustomEvent) => {
21
99
  event.stopPropagation();
22
- const currentTab = this.getSelectedTabId();
23
- if (currentTab === SPECIFICATION_TAB_TITLE) {
100
+ if (this.hasSpecificationContent()) {
24
101
  this.didScrollToSelectedHash = false;
25
102
  }
26
103
  };
27
104
 
28
105
  onTabChanged = () => {
29
106
  this.updateRNB();
30
- };
31
107
 
32
- private getTabPanelList() {
33
- // eslint-disable-next-line @lwc/lwc/no-document-query
34
- return document.querySelector("dx-tab-panel-list");
35
- }
108
+ const { hash } = window.location;
109
+ if (this.hasSpecificationContent() && hash) {
110
+ this.didScrollToSelectedHash = false;
111
+
112
+ requestAnimationFrame(() => this.updateHeadingForRNB());
113
+ }
114
+ };
36
115
 
37
116
  protected getHeadingElements() {
38
117
  let headingElements = super.getHeadingElements();
39
118
  if (this.showTabBasedRNB) {
40
- const tabPanelListItem: any = this.getTabPanelList();
119
+ const tabPanelListItem = this.getTabPanelList();
41
120
  const tabPanels =
42
121
  tabPanelListItem?.querySelectorAll("dx-tab-panel");
122
+
43
123
  for (const tabPanelItem of tabPanels) {
44
124
  if (tabPanelItem.active) {
45
125
  // This is needed for Specification tab content
@@ -65,6 +145,7 @@ export default class LwcContentLayout extends ContentLayout {
65
145
  private updateURL() {
66
146
  const tabs = this.getAllTabs();
67
147
  const selectedTabId = this.getSelectedTabId();
148
+
68
149
  tabs.forEach((tab: any) => {
69
150
  if (tab.getAttribute("aria-selected") === "true") {
70
151
  const tabID = tab.getAttribute("aria-label");
@@ -101,22 +182,38 @@ export default class LwcContentLayout extends ContentLayout {
101
182
  const selectedTabId = this.getSelectedTabId();
102
183
  if (selectedTabId) {
103
184
  this.selectTabById(selectedTabId);
185
+
186
+ // If there's a hash and we have specification content,
187
+ // we need to wait for the content to load before scrolling
188
+ const { hash } = window.location;
189
+ if (this.hasSpecificationContent() && hash) {
190
+ // Reset the scroll flag to allow scrolling once content is loaded
191
+ this.didScrollToSelectedHash = false;
192
+ }
104
193
  }
105
194
  });
106
195
  }
107
196
 
108
197
  private getAllTabs(): any[] {
109
- const tabPanelListItem: any = this.getTabPanelList();
198
+ // Return cached result if available
199
+ if (this.allTabsCache) {
200
+ return this.allTabsCache;
201
+ }
202
+
203
+ const tabPanelListItem = this.getTabPanelList();
110
204
  if (tabPanelListItem?.shadowRoot) {
111
- return Array.from(
205
+ this.allTabsCache = Array.from(
112
206
  tabPanelListItem.shadowRoot.querySelectorAll(
113
207
  "dx-tab-panel-item"
114
208
  )
115
209
  ).map((tabPanelItem: any) =>
116
210
  tabPanelItem.shadowRoot.querySelector("button")
117
211
  );
212
+ } else {
213
+ this.allTabsCache = [];
118
214
  }
119
- return [];
215
+
216
+ return this.allTabsCache;
120
217
  }
121
218
 
122
219
  private selectTabById(tabId: string) {
@@ -158,8 +255,12 @@ export default class LwcContentLayout extends ContentLayout {
158
255
  }
159
256
  }
160
257
 
258
+ onSlotChange(): void {
259
+ this.clearAllCaches();
260
+ super.onSlotChange();
261
+ }
262
+
161
263
  updateHeadingForRNB(): void {
162
- // We only need to update URL in case of /docs and ignore if tabs are used anywhere else in DSC
163
264
  if (this.showTabBasedRNB) {
164
265
  this.updateURL();
165
266
  }
@@ -3,14 +3,7 @@
3
3
  @import "docHelpers/status";
4
4
 
5
5
  :host {
6
- --doc-c-phase-top: 0;
7
6
  --doc-c-phase-container-align-items: flex-start;
8
-
9
- position: sticky;
10
- top: var(--doc-c-phase-top);
11
-
12
- /* NOTE: If you are changing z-index value here, ensure it's less than z-index of dx-sidebar in contentLayout.css */
13
- z-index: var(--dx-g-z-index-100);
14
7
  }
15
8
 
16
9
  .doc-phase-container {
@@ -0,0 +1,7 @@
1
+ :host {
2
+ --dx-footer-margin-top: 142px;
3
+ --doc-c-redoc-sidebar-top: calc(
4
+ var(--dx-g-global-header-height) + var(--dx-g-doc-header-height) +
5
+ var(--dx-g-spacing-xl)
6
+ );
7
+ }
@@ -0,0 +1,13 @@
1
+ <template>
2
+ <template lwc:if={showError}>
3
+ <dx-error
4
+ header="We lost communication with the space station."
5
+ subtitle="We encountered a server-related issue. (Don't worry, we're on it.) Refresh your browser or try again later."
6
+ image=""
7
+ code="500"
8
+ ></dx-error>
9
+ </template>
10
+ <template lwc:else>
11
+ <slot></slot>
12
+ </template>
13
+ </template>