@salesforcedevs/docs-components 0.57.1-callout-fix → 0.61.0

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.
@@ -0,0 +1,76 @@
1
+ const NAVIGATION_ITEMS = [
2
+ {
3
+ label: "Summary",
4
+ name: "summary",
5
+ childrenPropertyName: undefined,
6
+ type: "summary"
7
+ },
8
+ {
9
+ label: "Endpoints",
10
+ name: "endpoints",
11
+ childrenPropertyName: "endpoints",
12
+ type: "endpoint"
13
+ },
14
+ {
15
+ label: "Documentation",
16
+ name: "documentation",
17
+ childrenPropertyName: "docs",
18
+ type: "documentation"
19
+ },
20
+ {
21
+ label: "Types",
22
+ name: "types",
23
+ childrenPropertyName: "types",
24
+ type: "type"
25
+ },
26
+ {
27
+ label: "Security",
28
+ name: "security",
29
+ childrenPropertyName: "security",
30
+ type: "security"
31
+ }
32
+ ];
33
+
34
+ const URL_CONFIG = {
35
+ summary: {
36
+ urlIdentifer: "label"
37
+ },
38
+ endpoint: {
39
+ urlIdentifer: "path"
40
+ },
41
+ method: {
42
+ urlIdentifer: "label"
43
+ },
44
+ documentation: {
45
+ urlIdentifer: "label"
46
+ },
47
+ type: {
48
+ urlIdentifer: "label",
49
+ prefix: "type:"
50
+ },
51
+ security: {
52
+ urlIdentifer: "label",
53
+ prefix: "security:"
54
+ }
55
+ };
56
+
57
+ export const REFERENCE_TYPES = {
58
+ markdown: "markdown",
59
+ raml: "rest-raml",
60
+ oa2: "rest-oa2",
61
+ oa3: "rest-oa3"
62
+ };
63
+
64
+ const oldReferenceIdNewReferenceIdMap: Record<string, string> = {
65
+ "commerce-api-assignments": "assignments",
66
+ "commerce-api-campaigns": "campaigns",
67
+ "commerce-api-catalogs": "catalogs",
68
+ "cdn-zones": "cdn-api-process-apis",
69
+ "inventory-impex": "impex",
70
+ "inventory-reservations": "inventory-reservation-service",
71
+ "shopper-login-and-api-access-service": "shopper-login",
72
+ "shopper-login-and-api-access-service-admin": "slas-admin",
73
+ "einstein-recommendations": "einstein-api-quick-start-guide"
74
+ };
75
+
76
+ export { NAVIGATION_ITEMS, URL_CONFIG, oldReferenceIdNewReferenceIdMap };
@@ -2,6 +2,7 @@ import { Json } from "typings/custom";
2
2
 
3
3
  export interface AmfTopicType {
4
4
  referenceId: string;
5
+ parentReferencePath: string;
5
6
  amfId: string;
6
7
  elementId: string;
7
8
  type: string;
@@ -10,6 +11,7 @@ export interface AmfTopicType {
10
11
  export interface AmfMetadataTopic extends AmfTopicType {
11
12
  meta: string;
12
13
  identifier: string;
14
+ navTitle?: string;
13
15
  }
14
16
 
15
17
  export interface AmfMetaTopicType extends AmfTopicType {
@@ -53,11 +55,37 @@ export type DocPhaseEntry = {
53
55
  body: string;
54
56
  };
55
57
 
56
- export interface AmfConfig extends AmfModelRecord {
58
+ export type ReferenceType = "markdown" | "rest-raml" | "rest-oa2" | "rest-oa3";
59
+
60
+ /**
61
+ * Represents parsed topic for the sidebar.
62
+ */
63
+ export interface ParsedMarkdownTopic {
64
+ label: string;
65
+ name: string;
66
+ children: ParsedMarkdownTopic[];
67
+ link?: {
68
+ href: string;
69
+ target?: string;
70
+ };
71
+ }
72
+
73
+ export interface AmfConfig {
57
74
  id: string;
58
- amf: string;
59
75
  version?: string;
60
76
  docPhase?: DocPhaseEntry;
77
+ title: string;
78
+ href: string;
79
+ referenceType: ReferenceType;
80
+
81
+ // determines if a reference config is the current active and selected one.
82
+ isSelected: boolean;
83
+
84
+ // required for spec based references
85
+ amf?: string;
86
+
87
+ // required for markdown based references
88
+ topic?: ParsedMarkdownTopic;
61
89
  }
62
90
 
63
91
  export interface ParsedTopicModel {
@@ -84,10 +112,22 @@ export interface ReferenceVersion {
84
112
  label: string;
85
113
  deprecated?: boolean;
86
114
  selected?: boolean;
115
+ link: {
116
+ href: string;
117
+ };
87
118
  }
88
119
 
89
120
  export interface ReferenceSetConfig {
121
+ refId?: string;
90
122
  versionToRefMap?: Map<string, Array<AmfConfig>>;
91
123
  refList: Array<AmfConfig>;
92
- versions?: Array<ReferenceVersion>;
124
+ versions: Array<ReferenceVersion>;
125
+ }
126
+
127
+ export interface RouteMeta {
128
+ meta: string;
129
+ referenceId: string;
130
+ topicId: string;
131
+ //type is only for spec based references
132
+ type?: string;
93
133
  }
@@ -4,12 +4,13 @@
4
4
  :host {
5
5
  display: flex;
6
6
  align-items: center;
7
+ justify-content: center;
7
8
  width: fit-content;
8
9
  }
9
10
 
10
11
  :host(.breadcrumb_long) {
11
12
  /* ensure 30 character min-width */
12
- min-width: 245px;
13
+ min-width: 200px;
13
14
  }
14
15
 
15
16
  :host(.breadcrumb_back-arrow) {
@@ -4,6 +4,8 @@
4
4
  :host {
5
5
  --dx-c-breadcrumbs-title-color: var(--dx-g-blue-vibrant-20);
6
6
  --dx-c-breadcrumbs-breadcrumb-color: var(--dx-g-blue-vibrant-20);
7
+
8
+ font-family: var(--dx-g-font-sans);
7
9
  }
8
10
 
9
11
  nav {
@@ -36,7 +36,7 @@
36
36
  analytics-event={analyticsEventName}
37
37
  analytics-base-payload={analyticsBasePayload}
38
38
  href={breadcrumb.href}
39
- key={breadcrumb.label}
39
+ key={breadcrumb.id}
40
40
  label={breadcrumb.label}
41
41
  ></doc-breadcrumb-item>
42
42
  <span class="breadcrumb-item_slash" key={breadcrumb.label}>
@@ -8,10 +8,12 @@ type BreadcrumbConfig = {
8
8
  };
9
9
 
10
10
  const GAP = 8;
11
+
12
+ // Unit in pixels based on Salesforce Sans font-family.
11
13
  const CONSTANTS = {
12
- pixelPerCharacter: 7.2,
13
- pixelPerCrumbSpace: GAP * 2 + 4.6,
14
- minWidthPerCrumb: 245,
14
+ pixelPerCharacter: 7.7,
15
+ pixelPerCrumbSpace: GAP * 2 + 8.6,
16
+ minWidthPerCrumb: 200,
15
17
  dropdownWidth: 32
16
18
  };
17
19
 
@@ -29,15 +31,26 @@ export default class Breadcrumbs extends LightningElement {
29
31
  get breadcrumbs(): Breadcrumb[] {
30
32
  return this._breadcrumbs;
31
33
  }
34
+
32
35
  set breadcrumbs(value) {
33
- this._breadcrumbs = toJson(value) || [];
36
+ this.normalizeAndAssignBreadcrumbs(value);
34
37
  this.calculateBreadcrumbsConfigs();
35
38
  if (this.observer) {
36
39
  this.updateDropdownOptionAmount();
37
40
  }
38
41
  }
39
42
 
43
+ @api
44
+ get pixelPerCharacter(): number {
45
+ return this._pixelPerCharacter;
46
+ }
47
+
48
+ set pixelPerCharacter(value: number | string) {
49
+ this._pixelPerCharacter = +value;
50
+ }
51
+
40
52
  private _breadcrumbs: Breadcrumb[] = [];
53
+ private _pixelPerCharacter = CONSTANTS.pixelPerCharacter;
41
54
  private navWidth = 0;
42
55
  private observer: ResizeObserver | null = null;
43
56
  private breadcrumbConfigs: BreadcrumbConfig[] = [];
@@ -72,7 +85,7 @@ export default class Breadcrumbs extends LightningElement {
72
85
  private get dropdownOptions(): OptionWithLink[] {
73
86
  return this.breadcrumbs!.slice(1, this.dropdownOptionAmount! + 1).map(
74
87
  (link) => ({
75
- id: link.href!,
88
+ id: link.id!,
76
89
  label: link.label,
77
90
  link: { href: link.href! }
78
91
  })
@@ -118,6 +131,17 @@ export default class Breadcrumbs extends LightningElement {
118
131
  this.observer?.disconnect();
119
132
  }
120
133
 
134
+ private normalizeAndAssignBreadcrumbs(breadcrumbs?: Breadcrumb[] | string) {
135
+ if (!breadcrumbs) {
136
+ return;
137
+ }
138
+
139
+ this._breadcrumbs = toJson(breadcrumbs).map((crumb: Breadcrumb) => ({
140
+ ...crumb,
141
+ id: crumb.id || crumb.href
142
+ }));
143
+ }
144
+
121
145
  private updateDropdownOptionAmount(): void {
122
146
  this.dropdownOptionAmount = this.breadcrumbConfigs.find(
123
147
  ({ minWidth }) => minWidth <= this.navWidth
@@ -150,7 +174,8 @@ export default class Breadcrumbs extends LightningElement {
150
174
  (previousValue, element) =>
151
175
  previousValue +
152
176
  Math.min(
153
- element.label.length * CONSTANTS.pixelPerCharacter,
177
+ element.label.length *
178
+ (this.pixelPerCharacter || CONSTANTS.pixelPerCharacter),
154
179
  CONSTANTS.minWidthPerCrumb
155
180
  ),
156
181
  (breadcrumbs.length - 1) * CONSTANTS.pixelPerCrumbSpace + offset
@@ -150,7 +150,7 @@ export default class Content extends LightningElement {
150
150
  is: ContentCallout
151
151
  });
152
152
  const detailEls = calloutEl.querySelectorAll(
153
- "p, div.data, ol, ul, p+.codeSection, p~.codeSection, div >.codeSection, .mediaBd > span.ph"
153
+ "p, div.data, ol, ul, p+.codeSection, .mediaBd > span.ph"
154
154
  );
155
155
  detailEls.forEach((detailEl) => {
156
156
  if (detailEl.innerHTML.trim() !== "") {
@@ -159,7 +159,7 @@ export default class Content extends LightningElement {
159
159
  });
160
160
 
161
161
  let flag = 1;
162
- for (let i: number = 0; i < detailEls.length; i++) {
162
+ for (let i = 0; i < detailEls.length; i++) {
163
163
  flag &= detailEls[i].innerHTML.trim() === "";
164
164
  }
165
165
 
@@ -56,7 +56,7 @@ dx-toc {
56
56
  max-width: 275px;
57
57
  }
58
58
 
59
- dx-breadcrumbs {
59
+ doc-breadcrumbs {
60
60
  display: block;
61
61
  margin-bottom: var(--dx-g-spacing-2xl);
62
62
  }
@@ -68,10 +68,6 @@ dx-breadcrumbs {
68
68
  }
69
69
 
70
70
  @media screen and (max-width: 800px) {
71
- dx-breadcrumbs {
72
- display: none;
73
- }
74
-
75
71
  .content-body {
76
72
  margin-top: var(--dx-c-content-vertical-spacing);
77
73
  }
@@ -28,17 +28,10 @@
28
28
  <slot name="doc-phase"></slot>
29
29
  <div class="content-body-container">
30
30
  <div class="content-body">
31
- <dx-breadcrumbs
31
+ <doc-breadcrumbs
32
32
  if:true={breadcrumbs}
33
33
  breadcrumbs={breadcrumbs}
34
- truncate
35
- hide-current-location
36
- ></dx-breadcrumbs>
37
- <dx-breadcrumbs
38
- if:false={breadcrumbs}
39
- pathname={pathname}
40
- hide-current-location
41
- ></dx-breadcrumbs>
34
+ ></doc-breadcrumbs>
42
35
  <slot onslotchange={onSlotChange}></slot>
43
36
  </div>
44
37
  <div class="right-nav-bar is-sticky">
@@ -20,6 +20,7 @@ const HIGHLIGHTABLE_SELECTOR = [
20
20
  "th",
21
21
  "td"
22
22
  ].join(",");
23
+ const OBSERVER_ATTACH_WAIT_TIME = 500;
23
24
 
24
25
  export default class ContentLayout extends LightningElement {
25
26
  @api sidebarValue: string;
@@ -95,6 +96,8 @@ export default class ContentLayout extends LightningElement {
95
96
  target: window
96
97
  });
97
98
  private tocValue?: string = undefined;
99
+ private observerTimerId = null;
100
+ private didScrollToSelectedHash = false;
98
101
 
99
102
  get showToc(): boolean {
100
103
  return this.tocOptions && this.tocOptions.length > 0;
@@ -119,6 +122,18 @@ export default class ContentLayout extends LightningElement {
119
122
  }
120
123
  }
121
124
 
125
+ renderedCallback(): void {
126
+ /**
127
+ * Note: We are adding timeout because chrome is optimizing and not triggering recent renderedCallback though elements reference is changed
128
+ * Also we are considering recent renderedCallback
129
+ */
130
+ this.clearRenderObserverTimer();
131
+ this.observerTimerId = setTimeout(
132
+ this.attachInteractionObserver,
133
+ OBSERVER_ATTACH_WAIT_TIME
134
+ );
135
+ }
136
+
122
137
  disconnectedCallback(): void {
123
138
  this.disconnectObserver();
124
139
  window.removeEventListener(
@@ -126,19 +141,25 @@ export default class ContentLayout extends LightningElement {
126
141
  this.updateHighlighted
127
142
  );
128
143
  this.searchSyncer.dispose();
144
+ this.clearRenderObserverTimer();
129
145
  }
130
146
 
147
+ clearRenderObserverTimer = () => {
148
+ if (this.observerTimerId) {
149
+ clearTimeout(this.observerTimerId);
150
+ }
151
+ };
152
+
131
153
  updateHighlighted = (event: Event): void =>
132
154
  highlightTerms(
133
155
  this.querySelectorAll(HIGHLIGHTABLE_SELECTOR),
134
156
  (event as CustomEvent<string>).detail
135
157
  );
136
158
 
137
- onSlotChange(event: Event): void {
159
+ attachInteractionObserver = (): void => {
138
160
  if (!this.enableSlotChange) {
139
161
  return;
140
162
  }
141
-
142
163
  this.disconnectObserver();
143
164
  this.observer = new IntersectionObserver((entries) => {
144
165
  entries.forEach(
@@ -150,30 +171,54 @@ export default class ContentLayout extends LightningElement {
150
171
  this.calculateActualSection();
151
172
  });
152
173
 
153
- const anchoredTags = (event.target as HTMLSlotElement)
154
- .assignedElements()
155
- .filter(({ tagName }) => tagName === TOC_HEADER_TAG)
156
- .map((tag) => {
157
- tag.id = tag.hash;
158
- return tag;
159
- });
160
-
161
- this._tocOptions = anchoredTags.map((tag) => ({
162
- anchor: `#${tag.hash}`,
163
- id: tag.id,
164
- label: tag.title
165
- }));
166
-
167
- this.scrollToHash(anchoredTags);
168
-
169
- anchoredTags.forEach((section) => {
170
- const id = section.getAttribute("id");
174
+ // Note: We are doing document.querySelectorAll as a quick fix as we are not getting heading elements reference this.querySelectorAll
175
+ const headingElements = document.querySelectorAll(TOC_HEADER_TAG);
176
+ for (const headingElement of headingElements) {
177
+ // Add headingElements to intersectionObserver for highlighting respective RNB item when user scroll
178
+ const id = headingElement.getAttribute("id");
171
179
  this.anchoredElements[id] = {
172
180
  id,
173
181
  intersect: false
174
182
  };
175
- this.observer.observe(section);
176
- });
183
+ this.observer.observe(headingElement);
184
+ }
185
+ if (!this.didScrollToSelectedHash) {
186
+ this.didScrollToSelectedHash = true;
187
+ this.scrollToHash(headingElements);
188
+ }
189
+ };
190
+
191
+ onSlotChange(event: Event): void {
192
+ const slotElements = (
193
+ event.target as HTMLSlotElement
194
+ ).assignedElements();
195
+
196
+ if (slotElements.length) {
197
+ const slotContentElement = slotElements[0];
198
+ const headingElements =
199
+ slotContentElement.ownerDocument?.getElementsByTagName(
200
+ TOC_HEADER_TAG
201
+ );
202
+ for (const headingElement of headingElements) {
203
+ // Sometimes elements hash is not being set when slot content is wrapped with div
204
+ headingElement.hash =
205
+ headingElement.attributes.hash?.nodeValue;
206
+ }
207
+ const tocOptions = [];
208
+ for (const headingElement of headingElements) {
209
+ headingElement.id = headingElement.hash;
210
+
211
+ // Update tocOptions from anchorTags
212
+ const tocItem = {
213
+ anchor: `#${headingElement.hash}`,
214
+ id: headingElement.id,
215
+ label: headingElement.title
216
+ };
217
+ tocOptions.push(tocItem);
218
+ }
219
+
220
+ this._tocOptions = tocOptions;
221
+ }
177
222
  }
178
223
 
179
224
  private disconnectObserver(): void {
@@ -183,16 +228,16 @@ export default class ContentLayout extends LightningElement {
183
228
  }
184
229
  }
185
230
 
186
- private scrollToHash(anchoredTags: Array<Element>): void {
231
+ // eslint-disable-next-line no-undef
232
+ private scrollToHash(headingElements: NodeListOf<Element>): void {
187
233
  let { hash } = window.location;
188
-
189
234
  if (hash) {
190
235
  hash = hash.substr(1);
191
- const toScrollElement = anchoredTags.find(
192
- (element) => element.getAttribute("id") === hash
193
- );
194
- if (toScrollElement) {
195
- toScrollElement.scrollIntoView({ behavior: "auto" });
236
+ for (const headingElement of headingElements) {
237
+ if (headingElement.getAttribute("id") === hash) {
238
+ headingElement.scrollIntoView({ behavior: "auto" });
239
+ break;
240
+ }
196
241
  }
197
242
  }
198
243
  }
@@ -24,6 +24,7 @@ export type TreeNode = {
24
24
  name: string;
25
25
  children?: Array<TreeNode>;
26
26
  isExpanded?: boolean;
27
+ parent?: TreeNode;
27
28
  };
28
29
 
29
30
  type DropdownOption = {
@@ -109,3 +110,5 @@ export type ContentApiOptions = {
109
110
  version: string;
110
111
  language: string;
111
112
  };
113
+
114
+ export type TocMap = { [key: string]: TreeNode };
@@ -8,7 +8,8 @@ import {
8
8
  DocumentData,
9
9
  DocLanguage,
10
10
  DocVersion,
11
- TreeNode
11
+ TreeNode,
12
+ TocMap
12
13
  } from "./types";
13
14
  import { Language } from "typings/custom";
14
15
  import { getLanguageDisplayTextById } from "dxUtils/language";
@@ -100,26 +101,26 @@ export class FetchContent {
100
101
 
101
102
  private normalizeNavItem(
102
103
  navItem: ApiNavItem,
103
- tocMap: { [key: string]: TreeNode }
104
+ tocMap: TocMap,
105
+ parentNavItem?: TreeNode
104
106
  ): TreeNode {
105
107
  const name = this.calculateNavItemName(navItem, tocMap);
106
108
  const node: TreeNode = {
107
109
  label: navItem.text,
108
- name
110
+ name,
111
+ parent: parentNavItem
109
112
  };
113
+
110
114
  if (name) {
111
115
  tocMap[name] = node;
112
116
  }
113
117
  node.children = navItem.children?.map((child) =>
114
- this.normalizeNavItem(child, tocMap)
118
+ this.normalizeNavItem(child, tocMap, node)
115
119
  );
116
120
  return node;
117
121
  }
118
122
 
119
- private calculateNavItemName(
120
- navItem: ApiNavItem,
121
- tocMap: { [key: string]: TreeNode }
122
- ): string {
123
+ private calculateNavItemName(navItem: ApiNavItem, tocMap: TocMap): string {
123
124
  let href = navItem.a_attr?.href || "";
124
125
  if (href.includes("#")) {
125
126
  const [pathUrl] = href.split("#");
@@ -144,11 +145,13 @@ export class FetchContent {
144
145
  }
145
146
 
146
147
  private normalizeLanguage(language: ApiDocLanguage): DocLanguage {
147
-
148
148
  return (
149
149
  language && {
150
- label: getLanguageDisplayTextById(this.languages, language.locale) ||
151
- language.label,
150
+ label:
151
+ getLanguageDisplayTextById(
152
+ this.languages,
153
+ language.locale
154
+ ) || language.label,
152
155
  id: language.locale,
153
156
  code: language.code,
154
157
  url: language.url
@@ -3,6 +3,11 @@
3
3
  --button-primary-color-hover: var(--dx-g-blue-vibrant-40);
4
4
  }
5
5
 
6
+ doc-breadcrumbs {
7
+ display: block;
8
+ margin-bottom: var(--dx-g-spacing-2xl);
9
+ }
10
+
6
11
  dx-dropdown {
7
12
  --dx-c-dropdown-option-font-size: var(--dx-g-text-sm);
8
13
  }
@@ -25,6 +25,11 @@
25
25
  </dx-button>
26
26
  </dx-dropdown>
27
27
  </div>
28
+ <doc-breadcrumbs
29
+ if:true={breadcrumbs}
30
+ breadcrumbs={breadcrumbs}
31
+ pixel-per-character={breadcrumbPixelPerCharacter}
32
+ ></doc-breadcrumbs>
28
33
  <doc-content
29
34
  docs-data={docContent}
30
35
  page-reference={pageReference}