@salesforcedevs/docs-components 0.7.59-sppage-alpha1 → 0.7.76-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 (39) hide show
  1. package/lwc.config.json +3 -0
  2. package/package.json +9 -4
  3. package/src/modules/doc/amfReference/amfReference.css +0 -12
  4. package/src/modules/doc/amfReference/amfReference.html +1 -6
  5. package/src/modules/doc/amfReference/amfReference.ts +10 -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/breadcrumbs/breadcrumbs.ts +14 -23
  9. package/src/modules/doc/componentPlayground/componentPlayground.css +30 -0
  10. package/src/modules/doc/componentPlayground/componentPlayground.html +20 -0
  11. package/src/modules/doc/componentPlayground/componentPlayground.ts +97 -0
  12. package/src/modules/doc/content/content.html +1 -0
  13. package/src/modules/doc/content/content.ts +7 -33
  14. package/src/modules/doc/contentCallout/contentCallout.css +1 -0
  15. package/src/modules/doc/contentLayout/contentLayout.css +27 -123
  16. package/src/modules/doc/contentLayout/contentLayout.html +42 -36
  17. package/src/modules/doc/contentLayout/contentLayout.ts +152 -204
  18. package/src/modules/doc/contentMedia/contentMedia.css +1 -1
  19. package/src/modules/doc/header/header.html +8 -3
  20. package/src/modules/doc/header/header.ts +49 -10
  21. package/src/modules/doc/lwcContentLayout/lwcContentLayout.css +9 -0
  22. package/src/modules/doc/lwcContentLayout/lwcContentLayout.html +64 -0
  23. package/src/modules/doc/lwcContentLayout/lwcContentLayout.ts +269 -0
  24. package/src/modules/doc/phase/phase.css +0 -7
  25. package/src/modules/doc/redocReference/redocReference.css +7 -0
  26. package/src/modules/doc/redocReference/redocReference.html +13 -0
  27. package/src/modules/doc/redocReference/redocReference.ts +427 -0
  28. package/src/modules/doc/specificationContent/specificationContent.css +33 -0
  29. package/src/modules/doc/specificationContent/specificationContent.html +94 -16
  30. package/src/modules/doc/specificationContent/specificationContent.ts +131 -21
  31. package/src/modules/doc/versionPicker/versionPicker.html +2 -0
  32. package/src/modules/doc/xmlContent/xmlContent.css +0 -10
  33. package/src/modules/doc/xmlContent/xmlContent.html +11 -8
  34. package/src/modules/doc/xmlContent/xmlContent.ts +76 -57
  35. package/src/modules/docHelpers/amfStyle/amfStyle.css +0 -2
  36. package/src/modules/docHelpers/contentLayoutStyle/contentLayoutStyle.css +160 -0
  37. package/src/modules/docUtils/utils/__mocks__/coveo.analytics.ts +16 -0
  38. package/src/modules/docUtils/utils/coveo.analytics.d.ts +10 -0
  39. package/src/modules/docUtils/utils/utils.ts +1 -1
@@ -1,16 +1,33 @@
1
1
  import { api } from "lwc";
2
2
  import cx from "classnames";
3
- import type { OptionWithNested } from "typings/custom";
3
+ import type { OptionWithNested, DevCenterConfig } from "typings/custom";
4
4
  import { HeaderBase } from "dxBaseElements/headerBase";
5
- import { toJson } from "dxUtils/normalizers";
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";
9
10
 
11
+ const isStorybook = () => {
12
+ const { host } = window.location;
13
+ return (
14
+ host === "localhost:6006" || /^dsc-comp.*\.herokuapp\.com$/.test(host)
15
+ );
16
+ };
17
+
10
18
  export default class Header extends HeaderBase {
11
19
  @api langValuePath: string = "id"; // allows to override how language property is interpreted, follows valuePath dropdown api.
12
20
  @api headerHref: string = "/";
13
21
 
22
+ @api
23
+ get hideDocHeader() {
24
+ return this._hideDocHeader;
25
+ }
26
+
27
+ set hideDocHeader(value) {
28
+ this._hideDocHeader = normalizeBoolean(value);
29
+ }
30
+
14
31
  @api
15
32
  get scopedNavItems() {
16
33
  return this._scopedNavItems;
@@ -21,7 +38,7 @@ export default class Header extends HeaderBase {
21
38
  }
22
39
 
23
40
  @api
24
- get devCenter() {
41
+ get devCenter(): DevCenterConfig {
25
42
  return this._devCenter;
26
43
  }
27
44
 
@@ -34,14 +51,27 @@ export default class Header extends HeaderBase {
34
51
  private tabletMatchMedia!: MediaQueryList;
35
52
  private shouldRender: boolean = false;
36
53
  private showDocDivider: boolean = false;
37
- private _devCenter: any;
54
+ private _devCenter!: DevCenterConfig;
55
+ private _hideDocHeader: boolean = false;
38
56
 
39
57
  protected mobileBreakpoint(): string {
40
58
  return MOBILE_MATCH;
41
59
  }
42
60
 
43
- private get hasScopedNavItems(): boolean {
44
- return this.scopedNavItems && this.scopedNavItems.length > 0;
61
+ private get showScopedNavItems(): boolean {
62
+ return (
63
+ this.scopedNavItems &&
64
+ this.scopedNavItems.length > 0 &&
65
+ !this.hideDocHeader
66
+ );
67
+ }
68
+
69
+ /**
70
+ * This function returns true if the hideDocHeader is true and the view is not mobile.
71
+ * Also we need to show the header border in case the doc is hidden or if the brand information doesn't exists.
72
+ */
73
+ private get shouldHideHeader(): boolean {
74
+ return (this.hideDocHeader && !this.mobile) || this.showDocDivider;
45
75
  }
46
76
 
47
77
  connectedCallback(): void {
@@ -53,12 +83,12 @@ export default class Header extends HeaderBase {
53
83
  this.tabletMatchMedia.addEventListener("change", this.onTabletChange);
54
84
 
55
85
  if (
56
- (window.location.pathname.includes("/docs/") &&
86
+ (!this.shouldHideHeader &&
87
+ window.location.pathname.includes("/docs/") &&
57
88
  window.location.pathname !== "/docs/apis") ||
58
89
  window.location.pathname ===
59
90
  "/tableau/embedding-playground/overview" ||
60
- window.location.host === "localhost:6006" ||
61
- window.location.host === "dsc-components.herokuapp.com"
91
+ isStorybook()
62
92
  ) {
63
93
  this.shouldRender = true;
64
94
  }
@@ -85,7 +115,16 @@ export default class Header extends HeaderBase {
85
115
  protected additionalClasses(): string {
86
116
  return cx(
87
117
  this.brand && "has-brand",
88
- this.hasScopedNavItems && "has-scoped-nav-items"
118
+ this.showScopedNavItems && "has-scoped-nav-items"
89
119
  );
90
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
+ }
91
130
  }
@@ -0,0 +1,9 @@
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
+ }
@@ -0,0 +1,64 @@
1
+ <template>
2
+ <div class="content">
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>
18
+ <div class="content-body-doc-phase-container">
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>
25
+ <div class="content-body-container">
26
+ <div class={contentBodyClasses}>
27
+ <doc-breadcrumbs
28
+ lwc:if={showBreadcrumbs}
29
+ breadcrumbs={breadcrumbs}
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>
46
+ <slot onslotchange={onSlotChange}></slot>
47
+ <doc-sprig-survey
48
+ lwc:if={shouldDisplayFeedback}
49
+ ></doc-sprig-survey>
50
+ </div>
51
+ <div lwc:if={showToc} class={rightNavBarClasses}>
52
+ <dx-toc
53
+ header={tocTitle}
54
+ options={tocOptions}
55
+ value={tocValue}
56
+ ></dx-toc>
57
+ </div>
58
+ </div>
59
+ <div lwc:if={showFooter} class="footer-container">
60
+ <dx-footer variant="no-signup"></dx-footer>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ </template>
@@ -0,0 +1,269 @@
1
+ import ContentLayout from "doc/contentLayout";
2
+ import cx from "classnames";
3
+
4
+ const TOC_HEADER_TAG = "doc-heading";
5
+ const RNB_BY_TAB = "docs-tab";
6
+ const SPECIFICATION_TAG = "doc-specification-content";
7
+ export const OBSERVER_ATTACH_WAIT_TIME = 500;
8
+
9
+ export default class LwcContentLayout extends ContentLayout {
10
+ private rnbByTab: boolean = false;
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
+
18
+ private setRNBByTab() {
19
+ const tabPanelListItem = this.getTabPanelList();
20
+ this.rnbByTab = tabPanelListItem?.id === RNB_BY_TAB;
21
+ }
22
+
23
+ get showTabBasedRNB() {
24
+ return this.rnbByTab;
25
+ }
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
+
98
+ onRNBClick = (event: CustomEvent) => {
99
+ event.stopPropagation();
100
+ if (this.hasSpecificationContent()) {
101
+ this.didScrollToSelectedHash = false;
102
+ }
103
+ };
104
+
105
+ onTabChanged = () => {
106
+ this.updateRNB();
107
+
108
+ const { hash } = window.location;
109
+ if (this.hasSpecificationContent() && hash) {
110
+ this.didScrollToSelectedHash = false;
111
+
112
+ requestAnimationFrame(() => this.updateHeadingForRNB());
113
+ }
114
+ };
115
+
116
+ protected getHeadingElements() {
117
+ let headingElements = super.getHeadingElements();
118
+ if (this.showTabBasedRNB) {
119
+ const tabPanelListItem = this.getTabPanelList();
120
+ const tabPanels =
121
+ tabPanelListItem?.querySelectorAll("dx-tab-panel");
122
+
123
+ for (const tabPanelItem of tabPanels) {
124
+ if (tabPanelItem.active) {
125
+ // This is needed for Specification tab content
126
+ const specificationElement = tabPanelItem.querySelector(
127
+ "doc-specification-content"
128
+ );
129
+ if (specificationElement) {
130
+ headingElements =
131
+ specificationElement.shadowRoot.querySelectorAll(
132
+ TOC_HEADER_TAG
133
+ );
134
+ } else {
135
+ headingElements =
136
+ tabPanelItem.querySelectorAll(TOC_HEADER_TAG);
137
+ }
138
+ break;
139
+ }
140
+ }
141
+ }
142
+ return headingElements;
143
+ }
144
+
145
+ private updateURL() {
146
+ const tabs = this.getAllTabs();
147
+ const selectedTabId = this.getSelectedTabId();
148
+
149
+ tabs.forEach((tab: any) => {
150
+ if (tab.getAttribute("aria-selected") === "true") {
151
+ const tabID = tab.getAttribute("aria-label");
152
+ const url = new URL(window.location.href);
153
+ if (selectedTabId !== tabID) {
154
+ url.searchParams.set("type", tabID);
155
+ url.hash = "";
156
+ window.history.pushState({}, "", url.toString());
157
+ }
158
+ }
159
+ });
160
+ }
161
+
162
+ // This event gets triggered when navigating back/forward
163
+ handlePopState = (): void => {
164
+ if (this.showTabBasedRNB) {
165
+ this.restoreTabSelection();
166
+ }
167
+ };
168
+
169
+ connectedCallback(): void {
170
+ super.connectedCallback();
171
+ window.addEventListener("popstate", this.handlePopState);
172
+ }
173
+
174
+ private getSelectedTabId() {
175
+ const urlParams = new URLSearchParams(window.location.search);
176
+ const selectedTabId = urlParams.get("type");
177
+ return selectedTabId;
178
+ }
179
+
180
+ private restoreTabSelection() {
181
+ requestAnimationFrame(() => {
182
+ const selectedTabId = this.getSelectedTabId();
183
+ if (selectedTabId) {
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
+ }
193
+ }
194
+ });
195
+ }
196
+
197
+ private getAllTabs(): any[] {
198
+ // Return cached result if available
199
+ if (this.allTabsCache) {
200
+ return this.allTabsCache;
201
+ }
202
+
203
+ const tabPanelListItem = this.getTabPanelList();
204
+ if (tabPanelListItem?.shadowRoot) {
205
+ this.allTabsCache = Array.from(
206
+ tabPanelListItem.shadowRoot.querySelectorAll(
207
+ "dx-tab-panel-item"
208
+ )
209
+ ).map((tabPanelItem: any) =>
210
+ tabPanelItem.shadowRoot.querySelector("button")
211
+ );
212
+ } else {
213
+ this.allTabsCache = [];
214
+ }
215
+
216
+ return this.allTabsCache;
217
+ }
218
+
219
+ private selectTabById(tabId: string) {
220
+ const tabs = this.getAllTabs();
221
+ tabs.forEach((tab: any) => {
222
+ if (tab.getAttribute("aria-label") === tabId) {
223
+ tab.click();
224
+ }
225
+ });
226
+ }
227
+
228
+ postRenderedCallback(): void {
229
+ this.setRNBByTab();
230
+ if (this.showTabBasedRNB) {
231
+ window.addEventListener("tabchanged", this.onTabChanged);
232
+ window.addEventListener(
233
+ "specificationdatarendered",
234
+ this.onTabChanged
235
+ );
236
+ window.addEventListener("selectedcontent", (event) =>
237
+ this.onRNBClick(event as CustomEvent)
238
+ );
239
+ this.restoreTabSelection();
240
+ }
241
+ }
242
+
243
+ disconnectedCallback(): void {
244
+ super.disconnectedCallback();
245
+ if (this.showTabBasedRNB) {
246
+ window.removeEventListener("tabchanged", this.onTabChanged);
247
+ window.removeEventListener(
248
+ "specificationdatarendered",
249
+ this.onTabChanged
250
+ );
251
+ window.removeEventListener("selectedcontent", (event) =>
252
+ this.onRNBClick(event as CustomEvent)
253
+ );
254
+ window.removeEventListener("popstate", this.handlePopState);
255
+ }
256
+ }
257
+
258
+ onSlotChange(): void {
259
+ this.clearAllCaches();
260
+ super.onSlotChange();
261
+ }
262
+
263
+ updateHeadingForRNB(): void {
264
+ if (this.showTabBasedRNB) {
265
+ this.updateURL();
266
+ }
267
+ super.updateHeadingForRNB();
268
+ }
269
+ }
@@ -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>