@salesforcedevs/docs-components 0.56.2-comp-flex-ref-1 → 0.56.2-example

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 (77) hide show
  1. package/lwc.config.json +10 -2
  2. package/package.json +18 -12
  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 +386 -102
  7. package/src/modules/doc/amfReference/types.ts +5 -12
  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.css +2 -1
  13. package/src/modules/doc/breadcrumbItem/breadcrumbItem.html +1 -1
  14. package/src/modules/doc/breadcrumbItem/breadcrumbItem.ts +22 -0
  15. package/src/modules/doc/breadcrumbs/breadcrumbs.css +9 -1
  16. package/src/modules/doc/breadcrumbs/breadcrumbs.html +44 -34
  17. package/src/modules/doc/breadcrumbs/breadcrumbs.ts +62 -23
  18. package/src/modules/doc/componentPlayground/componentPlayground.css +22 -0
  19. package/src/modules/doc/componentPlayground/componentPlayground.html +20 -0
  20. package/src/modules/doc/componentPlayground/componentPlayground.ts +42 -0
  21. package/src/modules/doc/content/content.css +70 -76
  22. package/src/modules/doc/content/content.html +1 -0
  23. package/src/modules/doc/content/content.ts +26 -47
  24. package/src/modules/doc/contentCallout/contentCallout.css +15 -7
  25. package/src/modules/doc/contentCallout/contentCallout.html +13 -4
  26. package/src/modules/doc/contentCallout/contentCallout.ts +14 -2
  27. package/src/modules/doc/contentLayout/contentLayout.css +1 -100
  28. package/src/modules/doc/contentLayout/contentLayout.html +30 -17
  29. package/src/modules/doc/contentLayout/contentLayout.ts +315 -70
  30. package/src/modules/doc/doDont/doDont.css +47 -0
  31. package/src/modules/doc/doDont/doDont.html +27 -0
  32. package/src/modules/doc/doDont/doDont.ts +17 -0
  33. package/src/modules/doc/header/header.css +65 -36
  34. package/src/modules/doc/header/header.html +41 -139
  35. package/src/modules/doc/header/header.ts +54 -76
  36. package/src/modules/doc/heading/heading.css +16 -37
  37. package/src/modules/doc/heading/heading.html +4 -4
  38. package/src/modules/doc/heading/heading.ts +12 -10
  39. package/src/modules/doc/headingAnchor/headingAnchor.css +2 -2
  40. package/src/modules/doc/headingAnchor/headingAnchor.ts +2 -2
  41. package/src/modules/doc/headingContent/headingContent.css +6 -8
  42. package/src/modules/doc/headingContent/headingContent.html +9 -15
  43. package/src/modules/doc/headingContent/headingContent.ts +2 -14
  44. package/src/modules/doc/lwcContentLayout/lwcContentLayout.css +1 -0
  45. package/src/modules/doc/lwcContentLayout/lwcContentLayout.html +68 -0
  46. package/src/modules/doc/lwcContentLayout/lwcContentLayout.ts +256 -0
  47. package/src/modules/doc/overview/overview.css +40 -0
  48. package/src/modules/doc/overview/overview.html +34 -0
  49. package/src/modules/doc/overview/overview.ts +12 -0
  50. package/src/modules/doc/phase/phase.css +18 -3
  51. package/src/modules/doc/phase/phase.html +15 -3
  52. package/src/modules/doc/phase/phase.ts +44 -8
  53. package/src/modules/doc/specificationContent/specificationContent.css +36 -0
  54. package/src/modules/doc/specificationContent/specificationContent.html +167 -0
  55. package/src/modules/doc/specificationContent/specificationContent.ts +127 -0
  56. package/src/modules/doc/sprigSurvey/sprigSurvey.html +20 -0
  57. package/src/modules/doc/sprigSurvey/sprigSurvey.scoped.css +16 -0
  58. package/src/modules/doc/sprigSurvey/sprigSurvey.ts +16 -0
  59. package/src/modules/doc/toc/toc.ts +1 -1
  60. package/src/modules/doc/versionPicker/versionPicker.css +64 -0
  61. package/src/modules/doc/versionPicker/versionPicker.html +38 -0
  62. package/src/modules/doc/versionPicker/versionPicker.ts +65 -0
  63. package/src/modules/doc/xmlContent/types.ts +12 -3
  64. package/src/modules/doc/xmlContent/utils.ts +17 -12
  65. package/src/modules/doc/xmlContent/xmlContent.css +32 -3
  66. package/src/modules/doc/xmlContent/xmlContent.html +41 -15
  67. package/src/modules/doc/xmlContent/xmlContent.ts +310 -96
  68. package/src/modules/docHelpers/amfStyle/amfStyle.css +10 -45
  69. package/src/modules/docHelpers/contentLayoutStyle/contentLayoutStyle.css +131 -0
  70. package/src/modules/docHelpers/imgStyle/imgStyle.css +59 -0
  71. package/src/modules/docHelpers/status/status.css +1 -1
  72. package/src/modules/docUtils/{SearchSyncer/SearchSyncer.ts → searchSyncer/searchSyncer.ts} +1 -0
  73. package/src/modules/docUtils/utils/__mocks__/coveo.analytics.ts +16 -0
  74. package/src/modules/docUtils/utils/coveo.analytics.d.ts +10 -0
  75. package/src/modules/docUtils/utils/utils.ts +32 -0
  76. package/src/modules/docBaseElements/lightningElementWithState/lightningElementWithState.ts +0 -93
  77. package/src/modules/docHelpers/phaseContentLayout/phaseContentLayout.css +0 -39
@@ -2,7 +2,9 @@ import { LightningElement, api, track } from "lwc";
2
2
  import { noCase } from "no-case";
3
3
  import { sentenceCase } from "sentence-case";
4
4
  import qs from "query-string";
5
- import { AmfModelParser } from "./utils";
5
+ import { AmfModelParser } from "doc/amfModelParser";
6
+ import { normalizeBoolean, toJson } from "dxUtils/normalizers";
7
+ import type { OptionWithLink } from "typings/custom";
6
8
  import type {
7
9
  AmfConfig,
8
10
  AmfMetadataTopic,
@@ -24,19 +26,35 @@ import {
24
26
  REFERENCE_TYPES,
25
27
  oldReferenceIdNewReferenceIdMap
26
28
  } from "./constants";
29
+ import { restoreScroll } from "dx/scrollManager";
30
+ import { DocPhaseInfo } from "typings/custom";
31
+ import { logCoveoPageView, oldVersionDocInfo } from "docUtils/utils";
32
+
33
+ type NavigationItem = {
34
+ label: string;
35
+ name: string;
36
+ isExpanded: boolean;
37
+ children: ParsedMarkdownTopic[];
38
+ isChildrenLoading: boolean;
39
+ };
27
40
 
28
41
  export default class AmfReference extends LightningElement {
29
- @api breadcrumbs?: string | null | undefined = null;
42
+ @api breadcrumbs: string | null = null;
30
43
  @api sidebarHeader!: string;
31
44
  @api coveoOrganizationId!: string;
32
45
  @api coveoPublicAccessToken!: string;
33
- @api coveoAdvancedQueryConfig!: string;
46
+ @api coveoAnalyticsToken!: string;
34
47
  @api coveoSearchHub!: string;
35
- @api useOldSidebar?: boolean = false;
48
+ @api useOldSidebar: boolean = false;
36
49
  @api tocTitle?: string;
37
50
  @api tocOptions?: string;
38
- @track navigation = [];
51
+ @api languages!: OptionWithLink[];
52
+ @api language!: string;
53
+ @api hideFooter = false;
54
+ @track navigation = [] as NavigationItem[];
39
55
  @track versions: Array<ReferenceVersion> = [];
56
+ @track showVersionBanner = false;
57
+ @track _coveoAdvancedQueryConfig!: { [key: string]: any };
40
58
 
41
59
  // Update this to update what component gets rendered in the content block
42
60
  @track
@@ -100,20 +118,30 @@ export default class AmfReference extends LightningElement {
100
118
  this.versions = this.getVersions();
101
119
  }
102
120
  this.selectedVersion = selectedVersion;
121
+ if (this.isSpecBasedReference(this._currentReferenceId)) {
122
+ this.isVersionFetched = true;
123
+ if (this.oldVersionInfo) {
124
+ this.showVersionBanner = true;
125
+ } else {
126
+ this.latestVersion = true;
127
+ }
128
+ }
129
+ } else {
130
+ this.isVersionFetched = true;
103
131
  }
104
132
 
105
133
  // This is to check if the url is hash based and redirect if needed
106
134
  const redirectUrl = this.getHashBasedRedirectUrl();
107
135
  if (redirectUrl) {
108
- location.href = redirectUrl;
136
+ window.location.href = redirectUrl;
109
137
  } else {
110
138
  this.updateAmfConfigInView();
111
139
  }
112
140
  }
113
141
 
114
142
  @api
115
- get docPhaseInfo() {
116
- return this.selectedReferenceDocPhase;
143
+ get docPhaseInfo(): string | null {
144
+ return this.selectedReferenceDocPhase || null;
117
145
  }
118
146
 
119
147
  set docPhaseInfo(value: string) {
@@ -123,18 +151,56 @@ export default class AmfReference extends LightningElement {
123
151
  }
124
152
  }
125
153
 
154
+ @api
155
+ get expandChildren() {
156
+ return this._expandChildren;
157
+ }
158
+
159
+ set expandChildren(value) {
160
+ this._expandChildren = normalizeBoolean(value);
161
+ }
162
+
163
+ /*
164
+ * The get coveoAdvancedQueryConfig() method returns this._coveoAdvancedQueryConfig,
165
+ * but before returning it, it checks if there are multiple versions (this.versions.length > 1)
166
+ * and if a version is selected (this.selectedVersion). If both conditions are met,
167
+ * it updates the version property of this._coveoAdvancedQueryConfig with the selected version.
168
+ */
169
+ @api
170
+ get coveoAdvancedQueryConfig(): { [key: string]: any } {
171
+ const coveoConfig = this._coveoAdvancedQueryConfig;
172
+ if (this.versions.length > 1 && this.selectedVersion) {
173
+ const currentGAVersionRef = this.versions[0];
174
+ if (this.selectedVersion.id !== currentGAVersionRef.id) {
175
+ // Currently Coveo only supports query without "v"
176
+ const version = this.selectedVersion.id.replace("v", "");
177
+ coveoConfig.version = version;
178
+ this._coveoAdvancedQueryConfig = coveoConfig;
179
+ }
180
+ }
181
+ return this._coveoAdvancedQueryConfig;
182
+ }
183
+
184
+ set coveoAdvancedQueryConfig(config) {
185
+ this._coveoAdvancedQueryConfig = toJson(config);
186
+ }
187
+
188
+ private get enableFooter(): boolean {
189
+ return !this.hideFooter;
190
+ }
191
+
126
192
  // model
127
193
  protected _amfConfigList: AmfConfig[] = [];
128
194
  protected _amfConfigMap: Map<string, AmfConfig> = new Map();
129
195
  protected _referenceSetConfig!: ReferenceSetConfig;
130
196
  protected _currentReferenceId = "";
131
197
 
132
- protected parentReferenceUrls = [];
198
+ protected parentReferenceUrls = [] as string[];
133
199
  protected amfMap: Record<string, AmfModelRecord> = {};
134
- protected amfFetchPromiseMap = {};
200
+ protected amfFetchPromiseMap = {} as any;
135
201
  protected metadata: { [key: string]: AmfMetadataTopic } = {};
136
202
  protected selectedTopic?: AmfMetaTopicType = undefined;
137
- protected selectedSidebarValue = undefined;
203
+ protected selectedSidebarValue: string | undefined = undefined;
138
204
 
139
205
  protected selectedVersion: ReferenceVersion | null = null;
140
206
 
@@ -142,6 +208,9 @@ export default class AmfReference extends LightningElement {
142
208
 
143
209
  private isParentLevelDocPhaseEnabled = false;
144
210
  private selectedReferenceDocPhase?: string | null = null;
211
+ private _expandChildren?: boolean = false;
212
+ private isVersionFetched = false;
213
+ private latestVersion = false;
145
214
 
146
215
  /**
147
216
  * Key for storing the currently selected reference url. This will be used to save the
@@ -154,6 +223,7 @@ export default class AmfReference extends LightningElement {
154
223
 
155
224
  constructor() {
156
225
  super();
226
+
157
227
  this._boundOnApiNavigationChanged =
158
228
  this.onApiNavigationChanged.bind(this);
159
229
  this._boundUpdateSelectedItemFromUrlQuery =
@@ -209,13 +279,14 @@ export default class AmfReference extends LightningElement {
209
279
  if (strippedHashItems.length) {
210
280
  const referenceId = strippedHashItems[0];
211
281
  const meta = strippedHashItems[1];
282
+ const encodedMeta = this.getUrlEncoded(meta);
212
283
  const updatedReferenceId =
213
284
  oldReferenceIdNewReferenceIdMap[referenceId];
214
285
  const newReferenceId = updatedReferenceId || referenceId;
215
286
  const referenceItemConfig =
216
287
  this.getAmfConfigWithId(newReferenceId);
217
288
  if (referenceItemConfig) {
218
- hashBasedRedirectUrl = `${referenceItemConfig.href}?meta=${meta}`;
289
+ hashBasedRedirectUrl = `${referenceItemConfig.href}?meta=${encodedMeta}`;
219
290
  }
220
291
  }
221
292
  }
@@ -268,6 +339,17 @@ export default class AmfReference extends LightningElement {
268
339
  return referenceId;
269
340
  }
270
341
 
342
+ private get oldVersionInfo(): DocPhaseInfo | null {
343
+ let info = null;
344
+ if (this.versions.length > 1 && this.selectedVersion) {
345
+ const currentGAVersionRef = this.versions[0];
346
+ if (this.selectedVersion.id !== currentGAVersionRef.id) {
347
+ info = oldVersionDocInfo(currentGAVersionRef.link.href);
348
+ }
349
+ }
350
+ return info;
351
+ }
352
+
271
353
  /**
272
354
  * @returns versions to be shown in the dropdown
273
355
  * For markdown based specs, Adds selected markdown topic url to same references
@@ -298,13 +380,13 @@ export default class AmfReference extends LightningElement {
298
380
  /**
299
381
  * Returns the selected version or the first available version.
300
382
  */
301
- private getSelectedVersion(): ReferenceVersion {
383
+ private getSelectedVersion(): ReferenceVersion | null {
302
384
  const versions = this._referenceSetConfig?.versions || [];
303
385
  const selectedVersion = versions.find(
304
386
  (v: ReferenceVersion) => v.selected
305
387
  );
306
388
  // return a selected version if there is one, else return the first one.
307
- return selectedVersion || (versions.length && versions[0]);
389
+ return selectedVersion || (versions.length && versions[0]) || null;
308
390
  }
309
391
 
310
392
  private updateAmfConfigInView(): void {
@@ -318,9 +400,11 @@ export default class AmfReference extends LightningElement {
318
400
  }
319
401
  }
320
402
 
321
- private async fetchAmf(amfConfig): Promise<AmfModel | AmfModel[]> {
403
+ private async fetchAmf(
404
+ amfConfig: AmfConfig
405
+ ): Promise<AmfModel | AmfModel[]> {
322
406
  const { amf } = amfConfig;
323
- const response = await fetch(amf, {
407
+ const response = await fetch(amf!, {
324
408
  headers: {
325
409
  "Cache-Control": `max-age=86400`
326
410
  }
@@ -339,7 +423,7 @@ export default class AmfReference extends LightningElement {
339
423
  /**
340
424
  * Returns whether given url is parent reference path like ../example-project/references/reference-id
341
425
  */
342
- private isParentReferencePath(urlPath: string): boolean {
426
+ private isParentReferencePath(urlPath?: string | null): boolean {
343
427
  if (!urlPath) {
344
428
  return false;
345
429
  }
@@ -351,13 +435,28 @@ export default class AmfReference extends LightningElement {
351
435
  return parentReferenceIndex !== -1;
352
436
  }
353
437
 
438
+ /**
439
+ * Expands the children of Markdown-based References.
440
+ */
441
+ private expandChildrenForMarkdownReferences(
442
+ children: ParsedMarkdownTopic[]
443
+ ): void {
444
+ if (!children) {
445
+ return;
446
+ }
447
+ for (const childNode of children) {
448
+ childNode.isExpanded = true;
449
+ this.expandChildrenForMarkdownReferences(childNode.children);
450
+ }
451
+ }
452
+
354
453
  /**
355
454
  * Populates reference Items from amfConfigList and assigns it to navigation for sidebar
356
455
  */
357
456
  private populateReferenceItems(): void {
358
- const navAmfOrder = [];
457
+ const navAmfOrder = [] as NavigationItem[];
359
458
  for (const [index, amfConfig] of this._amfConfigList.entries()) {
360
- let navItemChildren = [];
459
+ let navItemChildren = [] as ParsedMarkdownTopic[];
361
460
  let isChildrenLoading = false;
362
461
  if (amfConfig.referenceType !== REFERENCE_TYPES.markdown) {
363
462
  if (amfConfig.isSelected) {
@@ -371,13 +470,24 @@ export default class AmfReference extends LightningElement {
371
470
  }
372
471
  isChildrenLoading = true;
373
472
  } else {
374
- navItemChildren = amfConfig.topic.children;
473
+ const isExpandChildrenEnabled = this.isExpandChildrenEnabled(
474
+ amfConfig.id
475
+ );
476
+ // check whether we should expand all the child nodes, this is required for Coveo to crawl.
477
+ if (isExpandChildrenEnabled) {
478
+ this.expandChildrenForMarkdownReferences(
479
+ amfConfig.topic!.children
480
+ );
481
+ }
482
+ navItemChildren = amfConfig.topic!.children;
375
483
  }
376
484
  // store nav items for each spec in order
377
485
  navAmfOrder[index] = {
378
486
  label: amfConfig.title,
379
487
  name: amfConfig.href,
380
- isExpanded: amfConfig.isSelected,
488
+ isExpanded:
489
+ amfConfig.isSelected ||
490
+ this.isExpandChildrenEnabled(amfConfig.id),
381
491
  children: navItemChildren,
382
492
  isChildrenLoading
383
493
  };
@@ -386,6 +496,15 @@ export default class AmfReference extends LightningElement {
386
496
  this.navigation = navAmfOrder;
387
497
  }
388
498
 
499
+ /**
500
+ * Returns a boolean indicating whether the children should be expanded or not.
501
+ */
502
+ private isExpandChildrenEnabled(referenceId: string): boolean {
503
+ return (
504
+ !!this.expandChildren && this._currentReferenceId === referenceId
505
+ );
506
+ }
507
+
389
508
  /**
390
509
  * Stores fetched AMF JSON value.
391
510
  * Creates and stores a new AmfModelParser instance for the AMF spec.
@@ -427,15 +546,18 @@ export default class AmfReference extends LightningElement {
427
546
  referenceId: string,
428
547
  items: ParsedTopicModel[]
429
548
  ): NavItem[] {
430
- const methodList = [];
549
+ const methodList = [] as NavItem[];
431
550
 
432
551
  items.forEach((item) => {
433
552
  item.methods?.forEach((method) => {
553
+ const title =
554
+ this.getTitleForLabel(method.label!) || method.method;
434
555
  const meta = this.addToMetadata(
435
556
  parentReferencePath,
436
557
  referenceId,
437
558
  "method",
438
- method
559
+ method,
560
+ title
439
561
  );
440
562
  methodList.push(
441
563
  Object.assign(method, {
@@ -443,8 +565,7 @@ export default class AmfReference extends LightningElement {
443
565
  parentReferencePath,
444
566
  meta
445
567
  ),
446
- label:
447
- this.getTitleForLabel(method.label) || method.method
568
+ label: title
448
569
  })
449
570
  );
450
571
  });
@@ -459,7 +580,9 @@ export default class AmfReference extends LightningElement {
459
580
  parentReferencePath: string,
460
581
  meta: string
461
582
  ): string {
462
- return meta ? `${parentReferencePath}?meta=${meta}` : "";
583
+ // update the encoded url meta param
584
+ const encodedMeta = meta ? this.getUrlEncoded(meta) : "";
585
+ return encodedMeta ? `${parentReferencePath}?meta=${encodedMeta}` : "";
463
586
  }
464
587
 
465
588
  /**
@@ -476,7 +599,8 @@ export default class AmfReference extends LightningElement {
476
599
  const parentReferencePath = amfConfig.href;
477
600
  const model = this.amfMap[referenceId].parser.parsedModel;
478
601
 
479
- const children = [];
602
+ const children: any[] = [];
603
+ const expandChildren = this.isExpandChildrenEnabled(referenceId);
480
604
 
481
605
  NAVIGATION_ITEMS.forEach(
482
606
  ({ label, name, childrenPropertyName, type }) => {
@@ -488,7 +612,8 @@ export default class AmfReference extends LightningElement {
488
612
  parentReferencePath,
489
613
  referenceId,
490
614
  type,
491
- summary
615
+ summary,
616
+ label
492
617
  );
493
618
  children.push({
494
619
  label,
@@ -501,8 +626,8 @@ export default class AmfReference extends LightningElement {
501
626
  }
502
627
  case "endpoint":
503
628
  if (
504
- model[childrenPropertyName] &&
505
- model[childrenPropertyName].length
629
+ model[childrenPropertyName!] &&
630
+ model[childrenPropertyName!].length
506
631
  ) {
507
632
  const amfTopicId = this.getFormattedIdentifier(
508
633
  referenceId,
@@ -511,7 +636,7 @@ export default class AmfReference extends LightningElement {
511
636
  const childTopics = this.assignEndpointNavItems(
512
637
  parentReferencePath,
513
638
  referenceId,
514
- model[childrenPropertyName]
639
+ model[childrenPropertyName!]
515
640
  );
516
641
  children.push({
517
642
  label,
@@ -519,30 +644,32 @@ export default class AmfReference extends LightningElement {
519
644
  parentReferencePath,
520
645
  this.metadata[amfTopicId]?.meta
521
646
  ),
522
- isExpanded: false,
647
+ isExpanded: expandChildren,
523
648
  children: childTopics
524
649
  });
525
650
  }
526
651
  break;
527
652
  case "security":
528
653
  case "type":
529
- if (model[childrenPropertyName]?.length) {
654
+ if (model[childrenPropertyName!]?.length) {
530
655
  // Sorting the types alphabetically
531
- model[childrenPropertyName].sort((typeA, typeB) => {
532
- const typeALbl = typeA.label.toLowerCase();
533
- const typeBLbl = typeB.label.toLowerCase();
534
- return typeALbl < typeBLbl
535
- ? -1
536
- : typeALbl > typeBLbl
537
- ? 1
538
- : 0;
539
- });
656
+ model[childrenPropertyName!].sort(
657
+ (typeA: any, typeB: any) => {
658
+ const typeALbl = typeA.label.toLowerCase();
659
+ const typeBLbl = typeB.label.toLowerCase();
660
+ return typeALbl < typeBLbl
661
+ ? -1
662
+ : typeALbl > typeBLbl
663
+ ? 1
664
+ : 0;
665
+ }
666
+ );
540
667
  }
541
668
  // eslint-disable-next-line no-fallthrough
542
669
  default:
543
670
  if (
544
- model[childrenPropertyName] &&
545
- model[childrenPropertyName].length
671
+ model[childrenPropertyName!] &&
672
+ model[childrenPropertyName!].length
546
673
  ) {
547
674
  const amfTopicId = this.getFormattedIdentifier(
548
675
  referenceId,
@@ -554,14 +681,15 @@ export default class AmfReference extends LightningElement {
554
681
  parentReferencePath,
555
682
  this.metadata[amfTopicId]?.meta
556
683
  ),
557
- isExpanded: false,
558
- children: model[childrenPropertyName].map(
559
- (topic) => {
684
+ isExpanded: expandChildren,
685
+ children: model[childrenPropertyName!].map(
686
+ (topic: any) => {
560
687
  const meta = this.addToMetadata(
561
688
  parentReferencePath,
562
689
  referenceId,
563
690
  type,
564
- topic
691
+ topic,
692
+ topic.label
565
693
  );
566
694
  return {
567
695
  label: topic.label,
@@ -590,17 +718,24 @@ export default class AmfReference extends LightningElement {
590
718
  parentReferencePath: string,
591
719
  referenceId: string,
592
720
  type: string,
593
- topic: { id: string; domId: string }
721
+ topic: { id: string; domId: string },
722
+ navTitle: string
594
723
  ): string {
595
- const { urlIdentifer, prefix } = URL_CONFIG[type];
724
+ const config = URL_CONFIG[type as keyof typeof URL_CONFIG];
725
+ const urlIdentifer = config.urlIdentifer;
726
+ let prefix = null;
727
+ if ("prefix" in config) {
728
+ prefix = config.prefix;
729
+ }
596
730
 
597
731
  // encodeURI to avoid special characters in the URL meta.
598
732
  const identifier =
599
- topic[urlIdentifer] && this.encodeIdentifier(topic[urlIdentifer]);
600
-
733
+ urlIdentifer in topic &&
734
+ this.encodeIdentifier(topic[urlIdentifer as keyof typeof topic]);
735
+ let meta;
601
736
  // Assuming that there will be an identifier always
602
737
  if (identifier) {
603
- const meta = prefix ? `${prefix}${identifier}` : `${identifier}`;
738
+ meta = prefix ? `${prefix}${identifier}` : `${identifier}`;
604
739
  this.metadata[meta] = {
605
740
  parentReferencePath,
606
741
  meta,
@@ -608,10 +743,11 @@ export default class AmfReference extends LightningElement {
608
743
  amfId: topic.id,
609
744
  elementId: topic.domId,
610
745
  identifier,
611
- type
746
+ type,
747
+ navTitle
612
748
  };
613
- return meta;
614
749
  }
750
+ return meta!;
615
751
  }
616
752
 
617
753
  /**
@@ -622,7 +758,7 @@ export default class AmfReference extends LightningElement {
622
758
  (metadata: AmfMetadataTopic) => {
623
759
  return routeMeta.meta === metadata.meta;
624
760
  }
625
- );
761
+ )!;
626
762
  }
627
763
 
628
764
  /**
@@ -636,7 +772,7 @@ export default class AmfReference extends LightningElement {
636
772
  return Object.values(this.metadata).find(
637
773
  (metadata: AmfMetadataTopic) =>
638
774
  referenceId === metadata.referenceId && amfId === metadata.amfId
639
- );
775
+ )!;
640
776
  }
641
777
 
642
778
  /**
@@ -651,7 +787,7 @@ export default class AmfReference extends LightningElement {
651
787
  (metadata: AmfMetadataTopic) =>
652
788
  referenceId === metadata.referenceId &&
653
789
  identifier === metadata.identifier
654
- );
790
+ )!;
655
791
  }
656
792
 
657
793
  /**
@@ -665,7 +801,7 @@ export default class AmfReference extends LightningElement {
665
801
  return Object.values(this.metadata).find(
666
802
  (metadata: AmfMetadataTopic) =>
667
803
  referenceId === metadata.referenceId && type === metadata.type
668
- );
804
+ )!;
669
805
  }
670
806
 
671
807
  /**
@@ -704,10 +840,13 @@ export default class AmfReference extends LightningElement {
704
840
  meta?: string
705
841
  ): void {
706
842
  if (meta) {
843
+ // update the encoded url meta param
844
+ const encodedMeta = this.getUrlEncoded(meta);
845
+
707
846
  window.history.pushState(
708
847
  {},
709
848
  "",
710
- `${parentReferencePath}?meta=${meta}`
849
+ `${parentReferencePath}?meta=${encodedMeta}`
711
850
  );
712
851
  }
713
852
  }
@@ -720,10 +859,13 @@ export default class AmfReference extends LightningElement {
720
859
  meta?: string
721
860
  ): void {
722
861
  if (meta) {
862
+ // update the encoded url meta param
863
+ const encodedMeta = this.getUrlEncoded(meta);
864
+
723
865
  window.history.replaceState(
724
- {},
866
+ window.history.state,
725
867
  "",
726
- `${parentReferencePath}?meta=${meta}`
868
+ `${parentReferencePath}?meta=${encodedMeta}`
727
869
  );
728
870
  }
729
871
  }
@@ -737,9 +879,8 @@ export default class AmfReference extends LightningElement {
737
879
  this._currentReferenceId
738
880
  );
739
881
  if (specBasedReference) {
740
- const currentMeta: RouteMeta | null = this.getReferenceMetaInfo(
741
- window.location.href
742
- );
882
+ const currentMeta: RouteMeta | undefined =
883
+ this.getReferenceMetaInfo(window.location.href);
743
884
  const metadata =
744
885
  currentMeta && this.getMetadataByUrlQuery(currentMeta);
745
886
  if (metadata) {
@@ -762,18 +903,20 @@ export default class AmfReference extends LightningElement {
762
903
  } else {
763
904
  this.loadMarkdownBasedReference();
764
905
  }
906
+
907
+ restoreScroll(); // don't try this at home kids
765
908
  }
766
909
 
767
910
  /**
768
911
  * The API Navigation event will always intend to navigate within the current reference
769
912
  * @param event
770
913
  */
771
- protected onApiNavigationChanged(event: CustomEvent): void {
914
+ protected onApiNavigationChanged(): void {
772
915
  const specBasedReference = this.isSpecBasedReference(
773
916
  this._currentReferenceId
774
917
  );
775
918
  if (specBasedReference) {
776
- const { meta } = this.selectedTopic;
919
+ const { meta } = this.selectedTopic!;
777
920
  const metadata = this.metadata[meta];
778
921
  if (metadata) {
779
922
  const {
@@ -847,14 +990,21 @@ export default class AmfReference extends LightningElement {
847
990
  }
848
991
 
849
992
  /**
850
- * @returns meta query param from given Url
993
+ * Returns the decoded meta query param from given Url as it is being used internally.
851
994
  */
852
995
  getMetaFromUrl(referenceUrl: string): string {
853
996
  const indexOfQueryParam = referenceUrl.indexOf("?");
854
997
  const urlPath = referenceUrl.substring(
855
998
  indexOfQueryParam >= 0 ? indexOfQueryParam : referenceUrl.length
856
999
  );
857
- return this.parseParams(urlPath)["meta"] as string;
1000
+ const meta = this.parseParams(urlPath).meta as string;
1001
+ // Always get the meta query param encoded and decode it and store it for internal use
1002
+ // This has 2 advantages,
1003
+ // 1. Supports backward compatible meta query param, so there is no need for redirects.
1004
+ // 2. Supports Prerender and Coveo for their crawling.
1005
+ const encodedMeta = meta && this.getUrlEncoded(meta);
1006
+ const decodedMeta = encodedMeta && decodeURIComponent(encodedMeta);
1007
+ return decodedMeta || "";
858
1008
  }
859
1009
 
860
1010
  /**
@@ -875,6 +1025,21 @@ export default class AmfReference extends LightningElement {
875
1025
  return meta;
876
1026
  }
877
1027
 
1028
+ /**
1029
+ * Gets the encoded url.
1030
+ * This method will return the encoded url for 2 cases,
1031
+ * 1. If the url is encoded already
1032
+ * 2. If the url is decoded
1033
+ */
1034
+ getUrlEncoded(url: string): string {
1035
+ // if url matches, then return the encoded url.
1036
+ if (decodeURIComponent(url) === url) {
1037
+ return encodeURIComponent(url);
1038
+ }
1039
+ // return the encoded url.
1040
+ return this.getUrlEncoded(decodeURIComponent(url));
1041
+ }
1042
+
878
1043
  /**
879
1044
  *
880
1045
  * @returns RouteMeta object for given referenceUrl
@@ -882,7 +1047,8 @@ export default class AmfReference extends LightningElement {
882
1047
  * For spec based references gets meta parm from url and then topicId & type from meta
883
1048
  * For markdown based references gets topicId as last html path in the name, meta & type will be empty
884
1049
  */
885
- getReferenceMetaInfo(referenceUrl: string): RouteMeta | undefined {
1050
+ getReferenceMetaInfo(referenceUrl: string | null): RouteMeta | undefined {
1051
+ let metaReferenceInfo;
886
1052
  if (referenceUrl) {
887
1053
  const referenceId = this.getReferenceIdFromUrl(referenceUrl);
888
1054
  let meta = "";
@@ -902,54 +1068,67 @@ export default class AmfReference extends LightningElement {
902
1068
  } else {
903
1069
  topicId = this.getMarkdownReferenceMeta(referenceUrl);
904
1070
  }
905
- return {
1071
+ metaReferenceInfo = {
906
1072
  referenceId,
907
1073
  meta,
908
1074
  topicId,
909
1075
  type
910
1076
  };
911
1077
  }
1078
+ return metaReferenceInfo;
912
1079
  }
913
1080
 
914
1081
  /**
915
- * Finds and returns referenceUrl if given topic url matches
1082
+ * Finds and returns referenceUrl and topicTitle if given topic url matches
916
1083
  */
917
- getReferenceUrlInGivenTopics(
1084
+ getReferenceDetailsInGivenTopics(
918
1085
  topics: ParsedMarkdownTopic[],
919
1086
  topicMeta: string
920
- ): string {
1087
+ ): { referenceUrl: string; topicTitle: string } {
921
1088
  let referenceUrl = "";
1089
+ let topicTitle = "";
922
1090
  for (let i = 0; i < topics.length; i++) {
923
1091
  const topic = topics[i];
924
- const meta = this.getMarkdownReferenceMeta(topic.link.href);
1092
+ const meta = this.getMarkdownReferenceMeta(topic.link!.href);
925
1093
  const childTopics = topic.children;
926
1094
  if (meta === topicMeta) {
927
- referenceUrl = topic.link.href;
1095
+ referenceUrl = topic.link!.href;
1096
+ topicTitle = topic.label;
928
1097
  } else if (childTopics && childTopics.length) {
929
- referenceUrl = this.getReferenceUrlInGivenTopics(
1098
+ const referenceDetails = this.getReferenceDetailsInGivenTopics(
930
1099
  childTopics,
931
1100
  topicMeta
932
1101
  );
1102
+ referenceUrl = referenceDetails.referenceUrl;
1103
+ topicTitle = referenceDetails.topicTitle;
933
1104
  }
934
- if (referenceUrl) {
1105
+ if (referenceUrl && topicTitle) {
935
1106
  break;
936
1107
  }
937
1108
  }
938
- return referenceUrl;
1109
+ return {
1110
+ referenceUrl,
1111
+ topicTitle
1112
+ };
939
1113
  }
940
1114
 
941
1115
  /**
942
- * Gives referenceUrl for given markdown topic url
1116
+ * Gives referenceUrl and topicTitle for given markdown topic url
943
1117
  */
944
- getRefLinkForGivenTopicMeta(
1118
+ getRefDetailsForGivenTopicMeta(
945
1119
  referenceId: string,
946
1120
  topicMeta: string
947
- ): string | undefined {
1121
+ ): { referenceUrl: string; topicTitle: string } | undefined {
948
1122
  const amfConfig = this.getAmfConfigWithId(referenceId);
1123
+ let referenceDetails;
949
1124
  if (amfConfig) {
950
1125
  const topics = amfConfig.topic?.children || [];
951
- return this.getReferenceUrlInGivenTopics(topics, topicMeta);
1126
+ referenceDetails = this.getReferenceDetailsInGivenTopics(
1127
+ topics,
1128
+ topicMeta
1129
+ );
952
1130
  }
1131
+ return referenceDetails;
953
1132
  }
954
1133
 
955
1134
  /**
@@ -1016,19 +1195,22 @@ export default class AmfReference extends LightningElement {
1016
1195
  "",
1017
1196
  selectedItemMetaData.meta
1018
1197
  );
1198
+
1019
1199
  this.updateUrlWithSelected(
1020
1200
  selectedItemMetaData.parentReferencePath,
1021
1201
  selectedItemMetaData.meta
1022
1202
  );
1203
+ this.updateTags(selectedItemMetaData.navTitle);
1023
1204
  }
1024
1205
  });
1025
1206
  } else {
1026
- let invalidTopicReferenceUrl = "";
1207
+ let invalidTopicReferenceUrl: string | null = "";
1027
1208
  if (topicId) {
1028
- const selectedItemUrl = this.getRefLinkForGivenTopicMeta(
1209
+ const referenceDetails = this.getRefDetailsForGivenTopicMeta(
1029
1210
  referenceId,
1030
1211
  topicId
1031
1212
  );
1213
+ const selectedItemUrl = referenceDetails?.referenceUrl;
1032
1214
  if (!selectedItemUrl) {
1033
1215
  invalidTopicReferenceUrl = previousRefUrlInSession;
1034
1216
  }
@@ -1045,7 +1227,7 @@ export default class AmfReference extends LightningElement {
1045
1227
  const referenceId = this.getReferenceIdFromUrl(url);
1046
1228
  const referenceItem = this.getAmfConfigWithId(referenceId);
1047
1229
  if (referenceItem) {
1048
- location.href = referenceItem.href;
1230
+ window.location.href = referenceItem.href;
1049
1231
  }
1050
1232
  }
1051
1233
 
@@ -1056,7 +1238,10 @@ export default class AmfReference extends LightningElement {
1056
1238
  * set selected sidebar value as a pathname
1057
1239
  */
1058
1240
 
1059
- private loadMarkdownBasedReference(referenceUrl?: string): void {
1241
+ private loadMarkdownBasedReference(referenceUrl?: string | null): void {
1242
+ // MILES TODO: figure out if we ever need to log a coveo page view in here
1243
+ // this would be the case if at some point we 'load' a new 'markdown based reference'
1244
+ // without actually triggering a page load
1060
1245
  let referenceId = "";
1061
1246
  const currentUrl = window.location.href;
1062
1247
  if (this.isProjectRootPath()) {
@@ -1070,7 +1255,7 @@ export default class AmfReference extends LightningElement {
1070
1255
  * CASE2: This case is to navigate to respective reference when the user clicked on root item
1071
1256
  * Ex: .../references/markdown-ref should navigate to first topic.
1072
1257
  */
1073
- referenceId = this.getReferenceIdFromUrl(referenceUrl);
1258
+ referenceId = this.getReferenceIdFromUrl(referenceUrl!);
1074
1259
  } else if (this.isParentReferencePath(currentUrl)) {
1075
1260
  /**
1076
1261
  * CASE3: This case is to navigate to respective reference when the user entered url with reference id
@@ -1084,39 +1269,66 @@ export default class AmfReference extends LightningElement {
1084
1269
  */
1085
1270
  const referenceMeta = this.getMarkdownReferenceMeta(referenceUrl);
1086
1271
  const selectedItemRefId = this.getReferenceIdFromUrl(referenceUrl);
1087
- const selectedItemUrl = this.getRefLinkForGivenTopicMeta(
1272
+ const referenceDetails = this.getRefDetailsForGivenTopicMeta(
1088
1273
  selectedItemRefId,
1089
1274
  referenceMeta
1090
1275
  );
1276
+ const selectedItemUrl = referenceDetails?.referenceUrl;
1091
1277
  if (!selectedItemUrl) {
1092
1278
  referenceId = this.getReferenceIdFromUrl(referenceUrl);
1093
1279
  }
1094
1280
  }
1095
1281
 
1282
+ let isRedirecting = false;
1096
1283
  if (referenceId) {
1097
1284
  const amfConfig = this.getAmfConfigWithId(referenceId);
1098
1285
  let redirectReferenceUrl = "";
1099
1286
  if (amfConfig) {
1100
- const childrenItems = amfConfig.topic.children;
1287
+ const childrenItems = amfConfig.topic!.children;
1101
1288
  if (childrenItems.length > 0) {
1102
- redirectReferenceUrl = childrenItems[0].link.href;
1289
+ redirectReferenceUrl = childrenItems[0].link!.href;
1103
1290
  }
1104
1291
  }
1105
1292
  if (redirectReferenceUrl) {
1106
1293
  if (this.isParentReferencePath(referenceUrl)) {
1107
1294
  // This is for CASE2 mentioned above, Where we need to navigate user to respective href
1108
- location.href = redirectReferenceUrl;
1295
+ isRedirecting = true;
1296
+ window.location.href = redirectReferenceUrl;
1109
1297
  } else {
1110
1298
  // This is for CASE 1,3 and 4 mentioned above, Where we need to update the browser history
1111
- window.history.replaceState({}, "", redirectReferenceUrl);
1299
+ window.history.replaceState(
1300
+ window.history.state,
1301
+ "",
1302
+ redirectReferenceUrl
1303
+ );
1112
1304
  }
1113
1305
  }
1114
1306
  }
1307
+ if (!isRedirecting) {
1308
+ const currentReferenceUrl = window.location.href;
1309
+ const referenceMeta =
1310
+ this.getMarkdownReferenceMeta(currentReferenceUrl);
1311
+ const selectedItemRefId =
1312
+ this.getReferenceIdFromUrl(currentReferenceUrl);
1313
+ const referenceDetails = this.getRefDetailsForGivenTopicMeta(
1314
+ selectedItemRefId,
1315
+ referenceMeta
1316
+ );
1317
+ if (referenceDetails) {
1318
+ this.updateTags(referenceDetails.topicTitle);
1319
+ }
1115
1320
 
1116
- this.versions = this.getVersions();
1321
+ this.versions = this.getVersions();
1322
+ if (this.oldVersionInfo) {
1323
+ this.showVersionBanner = true;
1324
+ } else {
1325
+ this.latestVersion = true;
1326
+ }
1117
1327
 
1118
- this.updateDocPhase();
1119
- this.selectedSidebarValue = window.location.pathname;
1328
+ this.isVersionFetched = true;
1329
+ this.updateDocPhase();
1330
+ this.selectedSidebarValue = window.location.pathname;
1331
+ }
1120
1332
  }
1121
1333
 
1122
1334
  /**
@@ -1130,11 +1342,71 @@ export default class AmfReference extends LightningElement {
1130
1342
  );
1131
1343
  }
1132
1344
 
1345
+ handleDismissVersionBanner() {
1346
+ this.showVersionBanner = false;
1347
+ }
1348
+
1349
+ private updateTags(navTitle = ""): void {
1350
+ if (!navTitle) {
1351
+ return;
1352
+ }
1353
+
1354
+ // this is required to update the nav title meta tag.
1355
+ // eslint-disable-next-line @lwc/lwc/no-document-query
1356
+ const metaNavTitle = document.querySelector('meta[name="nav-title"]');
1357
+ // eslint-disable-next-line @lwc/lwc/no-document-query
1358
+ const titleTag = document.querySelector("title");
1359
+ const TITLE_SEPARATOR = " | ";
1360
+
1361
+ if (metaNavTitle) {
1362
+ metaNavTitle.setAttribute("content", navTitle);
1363
+ }
1364
+
1365
+ /**
1366
+ * Right now, the title tag only changes when you pick a Ref spec,
1367
+ * not every time you choose a subsection of the Ref spec.
1368
+ * This update aims to refresh the title tag with each selection.
1369
+ * If a Ref spec is chosen, we add the value of the <selected topic> to the title.
1370
+ * If a subsection is selected, we update the first part of the current
1371
+ * title with the new <selected topic>.
1372
+ * Example: Following is a sample project structure.
1373
+ * - Project Name
1374
+ * - Ref Spec1
1375
+ * - Summary
1376
+ * - Endpoints
1377
+ * - E1
1378
+ * - E2
1379
+ * - Ref Spec2
1380
+ * - Summary
1381
+ * - Endpoints
1382
+ * - E1 (Selected)
1383
+ * - E2
1384
+ * Previous Title: Ref Spec2 | Project Name | Salesforce Developer
1385
+ * New Title: E1 | Ref Spec2 | Project Name | Salesforce Developer
1386
+ *
1387
+ */
1388
+ if (titleTag) {
1389
+ let titleTagValue = titleTag.textContent;
1390
+ const titleTagSectionValues: string[] =
1391
+ titleTagValue?.split(TITLE_SEPARATOR);
1392
+ if (titleTagSectionValues) {
1393
+ if (titleTagSectionValues.length <= 3) {
1394
+ titleTagValue = navTitle + TITLE_SEPARATOR + titleTagValue;
1395
+ } else {
1396
+ titleTagSectionValues[0] = navTitle;
1397
+ titleTagValue = titleTagSectionValues.join(TITLE_SEPARATOR);
1398
+ }
1399
+ }
1400
+ titleTag.textContent = titleTagValue;
1401
+ }
1402
+ }
1403
+
1133
1404
  onNavSelect(event: CustomEvent): void {
1134
1405
  const name = event.detail.name;
1135
1406
  if (name) {
1136
- const referenceId = this.getReferenceIdFromUrl(name);
1137
- const specBasedReference = this.isSpecBasedReference(referenceId);
1407
+ const urlReferenceId = this.getReferenceIdFromUrl(name);
1408
+ const specBasedReference =
1409
+ this.isSpecBasedReference(urlReferenceId);
1138
1410
  if (specBasedReference) {
1139
1411
  const metaVal = this.getMetaFromUrl(name);
1140
1412
  const currentSelectedMeta = this.selectedTopic
@@ -1163,7 +1435,13 @@ export default class AmfReference extends LightningElement {
1163
1435
  elementId,
1164
1436
  metaVal
1165
1437
  );
1438
+
1439
+ logCoveoPageView(
1440
+ this.coveoOrganizationId,
1441
+ this.coveoAnalyticsToken
1442
+ );
1166
1443
  this.updateUrlWithSelected(parentReferencePath, metaVal);
1444
+ this.updateTags(metadata.navTitle);
1167
1445
  } else {
1168
1446
  if (this.isParentReferencePath(name)) {
1169
1447
  this.loadNewReferenceItem(name);
@@ -1195,16 +1473,22 @@ export default class AmfReference extends LightningElement {
1195
1473
 
1196
1474
  handleSelectedItem(): void {
1197
1475
  // update topic view
1198
- const { referenceId, amfId, type } = this.selectedTopic;
1476
+ const { referenceId, amfId, type } = this.selectedTopic!;
1477
+
1478
+ // Adding stringify inside try/catch
1479
+ let amfModelString = "";
1480
+ try {
1481
+ amfModelString = JSON.stringify(this.amfMap[referenceId].model);
1482
+ } catch (error) {
1483
+ console.error(`Error stringifying amf model: ${error}`);
1484
+ }
1199
1485
 
1200
1486
  // This updates the component in the content section.
1201
1487
  this.topicModel = {
1202
1488
  type,
1203
- amf: this.amfMap[referenceId].model,
1489
+ amf: amfModelString,
1204
1490
  parser: this.amfMap[referenceId].parser,
1205
1491
  id: amfId
1206
1492
  };
1207
-
1208
- window.scrollTo({ top: 0, behavior: "smooth" });
1209
1493
  }
1210
1494
  }