@salesforcedevs/docs-components 0.0.1 → 0.0.2-edit

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 (83) hide show
  1. package/lwc.config.json +11 -2
  2. package/package.json +13 -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 +225 -92
  7. package/src/modules/doc/amfReference/types.ts +3 -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.ts +17 -10
  13. package/src/modules/doc/breadcrumbs/breadcrumbs.html +7 -9
  14. package/src/modules/doc/breadcrumbs/breadcrumbs.ts +30 -34
  15. package/src/modules/doc/chat/README.md +179 -0
  16. package/src/modules/doc/chat/chat.css +818 -0
  17. package/src/modules/doc/chat/chat.html +241 -0
  18. package/src/modules/doc/chat/chat.ts +586 -0
  19. package/src/modules/doc/componentPlayground/componentPlayground.css +22 -0
  20. package/src/modules/doc/componentPlayground/componentPlayground.html +20 -0
  21. package/src/modules/doc/componentPlayground/componentPlayground.ts +29 -0
  22. package/src/modules/doc/content/content.css +70 -76
  23. package/src/modules/doc/content/content.html +1 -0
  24. package/src/modules/doc/content/content.ts +24 -45
  25. package/src/modules/doc/contentCallout/contentCallout.css +10 -7
  26. package/src/modules/doc/contentCallout/contentCallout.html +11 -4
  27. package/src/modules/doc/contentCallout/contentCallout.ts +8 -1
  28. package/src/modules/doc/contentLayout/contentLayout.css +1 -98
  29. package/src/modules/doc/contentLayout/contentLayout.html +41 -19
  30. package/src/modules/doc/contentLayout/contentLayout.ts +298 -89
  31. package/src/modules/doc/doDont/doDont.css +47 -0
  32. package/src/modules/doc/doDont/doDont.html +27 -0
  33. package/src/modules/doc/doDont/doDont.ts +17 -0
  34. package/src/modules/doc/editFile/editFile.css +514 -0
  35. package/src/modules/doc/editFile/editFile.html +164 -0
  36. package/src/modules/doc/editFile/editFile.ts +213 -0
  37. package/src/modules/doc/header/header.css +65 -36
  38. package/src/modules/doc/header/header.html +41 -139
  39. package/src/modules/doc/header/header.ts +54 -76
  40. package/src/modules/doc/heading/heading.css +16 -37
  41. package/src/modules/doc/heading/heading.html +4 -4
  42. package/src/modules/doc/heading/heading.ts +12 -10
  43. package/src/modules/doc/headingAnchor/headingAnchor.css +2 -2
  44. package/src/modules/doc/headingAnchor/headingAnchor.ts +2 -2
  45. package/src/modules/doc/headingContent/headingContent.css +1 -1
  46. package/src/modules/doc/headingContent/headingContent.html +2 -2
  47. package/src/modules/doc/headingContent/headingContent.ts +2 -2
  48. package/src/modules/doc/lwcContentLayout/lwcContentLayout.css +1 -0
  49. package/src/modules/doc/lwcContentLayout/lwcContentLayout.html +68 -0
  50. package/src/modules/doc/lwcContentLayout/lwcContentLayout.ts +168 -0
  51. package/src/modules/doc/overview/overview.css +40 -0
  52. package/src/modules/doc/overview/overview.html +34 -0
  53. package/src/modules/doc/overview/overview.ts +12 -0
  54. package/src/modules/doc/phase/phase.css +18 -3
  55. package/src/modules/doc/phase/phase.html +12 -2
  56. package/src/modules/doc/phase/phase.ts +44 -8
  57. package/src/modules/doc/specificationContent/specificationContent.css +36 -0
  58. package/src/modules/doc/specificationContent/specificationContent.html +171 -0
  59. package/src/modules/doc/specificationContent/specificationContent.ts +127 -0
  60. package/src/modules/doc/sprigSurvey/sprigSurvey.html +20 -0
  61. package/src/modules/doc/sprigSurvey/sprigSurvey.scoped.css +16 -0
  62. package/src/modules/doc/sprigSurvey/sprigSurvey.ts +16 -0
  63. package/src/modules/doc/toc/toc.html +1 -3
  64. package/src/modules/doc/toc/toc.ts +1 -1
  65. package/src/modules/doc/toolbar/toolbar.ts +6 -6
  66. package/src/modules/doc/versionPicker/versionPicker.css +64 -0
  67. package/src/modules/doc/versionPicker/versionPicker.html +38 -0
  68. package/src/modules/doc/versionPicker/versionPicker.ts +65 -0
  69. package/src/modules/doc/xmlContent/types.ts +9 -3
  70. package/src/modules/doc/xmlContent/utils.ts +3 -1
  71. package/src/modules/doc/xmlContent/xmlContent.css +26 -4
  72. package/src/modules/doc/xmlContent/xmlContent.html +29 -17
  73. package/src/modules/doc/xmlContent/xmlContent.ts +212 -79
  74. package/src/modules/docHelpers/amfStyle/amfStyle.css +6 -6
  75. package/src/modules/docHelpers/contentLayoutStyle/contentLayoutStyle.css +131 -0
  76. package/src/modules/docHelpers/imgStyle/imgStyle.css +59 -0
  77. package/src/modules/docHelpers/status/status.css +1 -1
  78. package/src/modules/docUtils/{SearchSyncer/SearchSyncer.ts → searchSyncer/searchSyncer.ts} +1 -0
  79. package/src/modules/docUtils/utils/__mocks__/coveo.analytics.ts +16 -0
  80. package/src/modules/docUtils/utils/coveo.analytics.d.ts +10 -0
  81. package/src/modules/docUtils/utils/utils.ts +32 -0
  82. package/src/modules/docBaseElements/lightningElementWithState/lightningElementWithState.ts +0 -93
  83. package/src/modules/docHelpers/phaseContentLayout/phaseContentLayout.css +0 -39
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @lwc/lwc/no-document-query */
1
2
  import { api, track } from "lwc";
2
3
  import { normalizeBoolean } from "dxUtils/normalizers";
3
4
  import { FetchContent } from "./utils";
@@ -7,30 +8,44 @@ import {
7
8
  DocVersion,
8
9
  TreeNode,
9
10
  Header,
11
+ SiderbarFooter,
10
12
  HistoryState,
11
13
  PageReference,
12
14
  TocMap
13
15
  } from "./types";
14
- import { SearchSyncer } from "docUtils/SearchSyncer";
15
- import { LightningElementWithState } from "docBaseElements/lightningElementWithState";
16
- import { Breadcrumb, Language } from "typings/custom";
16
+ import { SearchSyncer } from "docUtils/searchSyncer";
17
+ import { LightningElementWithState } from "dxBaseElements/lightningElementWithState";
18
+ import { logCoveoPageView, oldVersionDocInfo } from "docUtils/utils";
19
+ import { Breadcrumb, DocPhaseInfo, Language } from "typings/custom";
20
+ import { track as trackGTM } from "dxUtils/analytics";
21
+ import DOMPurify from "dompurify";
17
22
 
18
23
  // TODO: Imitating from actual implementation as doc-content use it like this. We should refactor it later.
19
- const handleContentError = (error): void => console.log(error);
24
+ const handleContentError = (error: any): void => console.log(error);
20
25
 
21
26
  const PIXEL_PER_CHARACTER_MAP: { [key: string]: number } = {
22
27
  default: 7.7,
23
28
  "ja-jp": 12.5
24
29
  };
25
30
 
31
+ const defaultSidebarFooter: SiderbarFooter = {
32
+ bailHref: "",
33
+ bailLabel: "",
34
+ languages: [],
35
+ language: ""
36
+ };
26
37
  export default class DocXmlContent extends LightningElementWithState<{
27
38
  isFetchingDocument: boolean;
28
39
  isFetchingContent: boolean;
29
40
  lastHighlightedSearch: string;
41
+ internalLinkClicked: boolean;
30
42
  }> {
31
43
  @api apiDomain = "https://developer.salesforce.com";
32
44
  @api coveoOrganizationId!: string;
33
45
  @api coveoPublicAccessToken!: string;
46
+ @api coveoAnalyticsToken!: string;
47
+ @api coveoSearchHub!: string;
48
+ @api hideFooter = false;
34
49
 
35
50
  @api
36
51
  get allLanguages(): Array<Language> {
@@ -53,21 +68,27 @@ export default class DocXmlContent extends LightningElementWithState<{
53
68
  }
54
69
 
55
70
  private availableLanguages: Array<DocLanguage> = [];
56
- private availableVersions: Array<DocVersion> = [];
57
- private contentProvider: FetchContent;
71
+ @track private availableVersions: Array<DocVersion> = [];
72
+ private contentProvider?: FetchContent;
58
73
  private docContent = "";
59
- private language: DocLanguage = null;
74
+ private language?: DocLanguage | null = null;
60
75
  private loaded = false;
76
+ private _pageHeader?: Header;
61
77
  private pdfUrl = "";
62
78
  private tocMap: TocMap = {};
63
- private sidebarContent: Array<TreeNode> = null;
64
- private version: DocVersion = null;
79
+ private sidebarContent: Array<TreeNode> | null = null;
80
+ private version: DocVersion | null = null;
65
81
  private docTitle = "";
66
- private analyticsEvent = "custEv_ctaLinkClick";
67
82
  private _pathName = "";
68
- private _pageHeader?: Header;
69
83
  private listenerAttached = false;
70
84
  private _enableCoveo?: boolean = false;
85
+ private sidebarFooterContent: SiderbarFooter = { ...defaultSidebarFooter };
86
+ private latestVersion = false;
87
+ private previewVersion = false;
88
+
89
+ private get enableFooter(): boolean {
90
+ return !this.hideFooter;
91
+ }
71
92
 
72
93
  private searchSyncer = new SearchSyncer({
73
94
  callbacks: {
@@ -107,6 +128,37 @@ export default class DocXmlContent extends LightningElementWithState<{
107
128
  });
108
129
  private _allLanguages: Array<Language> = [];
109
130
 
131
+ private get oldVersionInfo(): DocPhaseInfo | null {
132
+ let info = null;
133
+ if (!this.disableVersion) {
134
+ const currentGAVersion = this.versionOptions.find(
135
+ (version) => !version.url.includes(version.id)
136
+ );
137
+ if (currentGAVersion?.link?.href && this.version?.id) {
138
+ const versionNo = currentGAVersion.id;
139
+ /**
140
+ * Need to show old version doc banner only if the version is less than the current ga version
141
+ * We should not show it to the preview version whose version is more than ga
142
+ **/
143
+ try {
144
+ if (parseFloat(this.version.id) < parseFloat(versionNo)) {
145
+ info = oldVersionDocInfo(currentGAVersion.link.href);
146
+ } else if (
147
+ parseFloat(this.version.id) > parseFloat(versionNo)
148
+ ) {
149
+ this.previewVersion = true;
150
+ }
151
+ } catch (exception) {
152
+ /* Ideally this use case should not happen, but just added to not to break the page*/
153
+ console.warn(exception);
154
+ }
155
+ }
156
+ }
157
+ return info;
158
+ }
159
+
160
+ @track showVersionBanner = false;
161
+
110
162
  @track private pageReference: PageReference = {};
111
163
  @track breadcrumbs: Array<Breadcrumb> = [];
112
164
 
@@ -142,8 +194,8 @@ export default class DocXmlContent extends LightningElementWithState<{
142
194
  renderedCallback(): void {
143
195
  this.setState({ internalLinkClicked: true });
144
196
  const urlSectionLink =
145
- this.pageReference?.hash?.split("#").length > 1
146
- ? this.pageReference.hash.split("#")[1]
197
+ this.pageReference?.hash?.split("#").length! > 1
198
+ ? this.pageReference.hash!.split("#")[1]
147
199
  : this.pageReference?.hash;
148
200
 
149
201
  const contentEl = this.template.querySelector("doc-content");
@@ -156,6 +208,7 @@ export default class DocXmlContent extends LightningElementWithState<{
156
208
 
157
209
  if (anchorEl) {
158
210
  anchorEl.scrollIntoView();
211
+
159
212
  this.setState({ internalLinkClicked: false });
160
213
  }
161
214
 
@@ -181,24 +234,17 @@ export default class DocXmlContent extends LightningElementWithState<{
181
234
  disconnectedCallback(): void {
182
235
  window.removeEventListener("popstate", this.handlePopState);
183
236
  this.searchSyncer.dispose();
184
- if (this.listenerAttached) {
185
- this.pageHeader.removeEventListener(
186
- "langchange",
187
- this.handleLanguageChange
188
- );
189
- this.listenerAttached = false;
190
- }
191
237
  }
192
238
 
193
- private get languageId(): string {
194
- return this.language.id.replace("-", "_");
239
+ private get languageId(): string | undefined {
240
+ return this.language?.id.replace("-", "_");
195
241
  }
196
242
 
197
- private get releaseVersionId(): string {
198
- return this.version.id;
243
+ private get releaseVersionId(): string | undefined {
244
+ return this.version?.id;
199
245
  }
200
246
 
201
- private get deliverable(): string {
247
+ private get deliverable(): string | undefined {
202
248
  return this.pageReference.deliverable;
203
249
  }
204
250
 
@@ -219,7 +265,11 @@ export default class DocXmlContent extends LightningElementWithState<{
219
265
  }
220
266
 
221
267
  private get coveoAdvancedQueryConfig(): CoveoAdvancedQueryXMLConfig {
222
- const config: { locale: string; topicid: string; version?: string } = {
268
+ const config: {
269
+ locale?: string;
270
+ topicid?: string;
271
+ version?: string;
272
+ } = {
223
273
  locale: this.languageId,
224
274
  topicid: this.deliverable
225
275
  };
@@ -233,7 +283,7 @@ export default class DocXmlContent extends LightningElementWithState<{
233
283
 
234
284
  private get pageHeader(): Header {
235
285
  if (!this._pageHeader) {
236
- this._pageHeader = document.querySelector("doc-header");
286
+ this._pageHeader = document.querySelector("doc-header")!;
237
287
  }
238
288
 
239
289
  return this._pageHeader;
@@ -276,13 +326,24 @@ export default class DocXmlContent extends LightningElementWithState<{
276
326
 
277
327
  private get breadcrumbPixelPerCharacter() {
278
328
  return (
279
- PIXEL_PER_CHARACTER_MAP[this.language.id] ||
329
+ PIXEL_PER_CHARACTER_MAP[this.language!.id] ||
280
330
  PIXEL_PER_CHARACTER_MAP.default
281
331
  );
282
332
  }
283
333
 
284
- private handlePopState = (): void =>
285
- this.updatePageReference(this.getReferenceFromUrl());
334
+ private get ANALYTICS_PAYLOAD() {
335
+ return {
336
+ element_title: "version picker",
337
+ content_category: "cta"
338
+ };
339
+ }
340
+
341
+ private handlePopState = (event: PopStateEvent): void =>
342
+ this.updatePageReference(this.getReferenceFromUrl(), event);
343
+
344
+ handleDismissVersionBanner() {
345
+ this.showVersionBanner = false;
346
+ }
286
347
 
287
348
  handleSelect(event: CustomEvent<{ name: string }>): void {
288
349
  event.stopPropagation();
@@ -314,7 +375,7 @@ export default class DocXmlContent extends LightningElementWithState<{
314
375
  this.updateUrl();
315
376
  }
316
377
 
317
- handleLanguageChange = (event: CustomEvent<string>): Promise<void> => {
378
+ handleLanguageChange = (event: any) => {
318
379
  if (this.language && this.language.id === event.detail) {
319
380
  return;
320
381
  }
@@ -322,12 +383,26 @@ export default class DocXmlContent extends LightningElementWithState<{
322
383
  this.language = this.availableLanguages.find(
323
384
  ({ id }) => id === event.detail
324
385
  );
325
- this.pageReference.docId = this.language.url;
386
+ this.pageReference.docId = this.language!.url;
387
+
388
+ trackGTM(event.target!, "custEv_ctaLinkClick", {
389
+ click_text: event.detail,
390
+ element_title: "language selector",
391
+ click_url: `${window.location.origin}${this.pageReferenceToString(
392
+ this.pageReference
393
+ )}`,
394
+ element_type: "link",
395
+ content_category: "cta"
396
+ });
397
+
326
398
  this.updateUrl();
327
399
  this.fetchDocument();
328
400
  };
329
401
 
330
- updatePageReference(newPageReference: PageReference): void {
402
+ updatePageReference(
403
+ newPageReference: PageReference,
404
+ event: PopStateEvent | undefined = undefined
405
+ ): void {
331
406
  this.pageReference.hash = newPageReference.hash;
332
407
  this.pageReference.search = newPageReference.search;
333
408
 
@@ -344,13 +419,26 @@ export default class DocXmlContent extends LightningElementWithState<{
344
419
  }
345
420
 
346
421
  this.fetchContent()
347
- .then(() => this.buildBreadcrumbs())
422
+ .then(() => {
423
+ this.buildBreadcrumbs();
424
+ document.body.scrollTop = event?.state?.scroll?.value || 0;
425
+ })
348
426
  .catch(handleContentError);
349
427
  }
350
428
 
429
+ private sanitizeUrlPart(part: string | undefined): string | undefined {
430
+ if (!part) {
431
+ return part;
432
+ }
433
+ return DOMPurify.sanitize(part);
434
+ }
435
+
351
436
  getReferenceFromUrl(): PageReference {
352
437
  const [page, docId, deliverable, contentDocumentId] =
353
- window.location.pathname.substr(1).split("/");
438
+ window.location.pathname
439
+ .substr(1)
440
+ .split("/")
441
+ .map(this.sanitizeUrlPart);
354
442
 
355
443
  const { origin: domain, hash, search } = window.location;
356
444
 
@@ -359,9 +447,9 @@ export default class DocXmlContent extends LightningElementWithState<{
359
447
  deliverable,
360
448
  docId,
361
449
  domain,
362
- hash,
450
+ hash: this.sanitizeUrlPart(hash),
363
451
  page,
364
- search
452
+ search: this.sanitizeUrlPart(search)
365
453
  };
366
454
  }
367
455
 
@@ -379,8 +467,9 @@ export default class DocXmlContent extends LightningElementWithState<{
379
467
  this.setState({
380
468
  isFetchingDocument: true
381
469
  });
382
- const data = await this.contentProvider.fetchDocumentData(
383
- this.pageReference.docId
470
+
471
+ const data = await this.contentProvider!.fetchDocumentData(
472
+ this.pageReference.docId!
384
473
  );
385
474
 
386
475
  // This could be a 404 scenario.
@@ -400,7 +489,7 @@ export default class DocXmlContent extends LightningElementWithState<{
400
489
  this.availableVersions = data.availableVersions;
401
490
  this.pdfUrl = data.pdfUrl;
402
491
 
403
- this.updateHeader();
492
+ this.updateHeaderAndSidebarFooter();
404
493
 
405
494
  this.buildBreadcrumbs();
406
495
 
@@ -409,6 +498,12 @@ export default class DocXmlContent extends LightningElementWithState<{
409
498
  this.updateUrl(HistoryState.REPLACE_STATE);
410
499
  }
411
500
 
501
+ if (this.oldVersionInfo) {
502
+ this.showVersionBanner = true;
503
+ } else {
504
+ this.latestVersion = true;
505
+ }
506
+
412
507
  if (
413
508
  this.pageReference?.contentDocumentId?.replace(/\.htm$/, "") !==
414
509
  data.id
@@ -438,12 +533,12 @@ export default class DocXmlContent extends LightningElementWithState<{
438
533
  this.setState({
439
534
  isFetchingContent: true
440
535
  });
441
- const data = await this.contentProvider.fetchContent(
442
- this.pageReference.deliverable,
443
- this.pageReference.contentDocumentId,
536
+ const data = await this.contentProvider!.fetchContent(
537
+ this.pageReference.deliverable!,
538
+ this.pageReference.contentDocumentId!,
444
539
  {
445
- language: this.language.id,
446
- version: this.version.id
540
+ language: this.language!.id,
541
+ version: this.version!.id
447
542
  }
448
543
  );
449
544
 
@@ -452,11 +547,7 @@ export default class DocXmlContent extends LightningElementWithState<{
452
547
  this.addMetatags();
453
548
 
454
549
  if (!this.pageReference.hash) {
455
- document.querySelector("main")?.scrollIntoView({
456
- behavior: "smooth",
457
- block: "start",
458
- inline: "nearest"
459
- });
550
+ document.body.scrollIntoView();
460
551
  }
461
552
  }
462
553
  this.setState({
@@ -464,7 +555,7 @@ export default class DocXmlContent extends LightningElementWithState<{
464
555
  });
465
556
  }
466
557
 
467
- updateHeader(): void {
558
+ updateHeaderAndSidebarFooter(): void {
468
559
  if (!this.pageHeader) {
469
560
  return;
470
561
  }
@@ -474,20 +565,12 @@ export default class DocXmlContent extends LightningElementWithState<{
474
565
  }
475
566
 
476
567
  if (this.pdfUrl) {
477
- this.pageHeader.bailHref = this.pdfUrl;
478
- this.pageHeader.bailLabel = "PDF";
479
- }
480
-
481
- if (!this.listenerAttached) {
482
- this.pageHeader.addEventListener(
483
- "langchange",
484
- this.handleLanguageChange
485
- );
486
- this.listenerAttached = true;
568
+ this.sidebarFooterContent.bailHref = this.pdfUrl;
569
+ this.sidebarFooterContent.bailLabel = "PDF";
487
570
  }
488
571
 
489
- this.pageHeader.languages = this.availableLanguages;
490
- this.pageHeader.language = this.language?.id;
572
+ this.sidebarFooterContent.languages = this.availableLanguages;
573
+ this.sidebarFooterContent.language = this.language?.id;
491
574
 
492
575
  if (this.pageReference) {
493
576
  const { docId, deliverable, page } = this.pageReference;
@@ -496,6 +579,7 @@ export default class DocXmlContent extends LightningElementWithState<{
496
579
  }
497
580
 
498
581
  updateUrl(method = HistoryState.PUSH_STATE): void {
582
+ logCoveoPageView(this.coveoOrganizationId, this.coveoAnalyticsToken);
499
583
  window.history[method](
500
584
  {},
501
585
  "docs",
@@ -511,23 +595,24 @@ export default class DocXmlContent extends LightningElementWithState<{
511
595
  }
512
596
 
513
597
  private updateSearchInput(searchParam: string): void {
514
- this.template
515
- .querySelector("doc-content-layout")
516
- ?.setSidebarInputValue(searchParam);
598
+ (this.refs.docContentLayout as any)?.setSidebarInputValue(searchParam);
517
599
  }
518
600
 
519
601
  private pageReferenceToString(reference: PageReference): string {
520
602
  const { page, docId, deliverable, contentDocumentId, hash, search } =
521
603
  reference;
522
604
  return `/${page}/${docId}/${deliverable}/${contentDocumentId}${this.normalizeSearch(
523
- search
605
+ search!
524
606
  )}${this.normalizeHash(hash)}`;
525
607
  }
526
608
 
527
- private normalizeUrlPart(part: string, sentinel: string): string {
609
+ private normalizeUrlPart(
610
+ part: string | undefined,
611
+ sentinel: string
612
+ ): string {
528
613
  return (
529
614
  (part &&
530
- (part.startsWith(sentinel) ? part : `${sentinel}${part}`)) ||
615
+ (part.startsWith(sentinel!) ? part : `${sentinel}${part}`)) ||
531
616
  ""
532
617
  );
533
618
  }
@@ -536,16 +621,16 @@ export default class DocXmlContent extends LightningElementWithState<{
536
621
  return this.normalizeUrlPart(search, "?");
537
622
  }
538
623
 
539
- private normalizeHash(hash: string): string {
624
+ private normalizeHash(hash?: string): string {
540
625
  return this.normalizeUrlPart(hash, "#");
541
626
  }
542
627
 
543
628
  private getComposedTitle(
544
- topicTitle: string | undefined,
629
+ topicTitle: string | null | undefined,
545
630
  docTitle: string | undefined
546
631
  ): string {
547
632
  // map to avoid duplicates
548
- const titleMap = {};
633
+ const titleMap: { [key: string]: any } = {};
549
634
  if (topicTitle) {
550
635
  // sometimes the h1 tag text (which is docSubTitle) contains text with new line character. For e.g, "Bulk API 2.0 Older\n Documentation",
551
636
  // here it contains \n in the text context which needs to be removed
@@ -580,10 +665,6 @@ export default class DocXmlContent extends LightningElementWithState<{
580
665
  return this.breadcrumbs && this.breadcrumbs.length > 1;
581
666
  }
582
667
 
583
- get docContentStyle(): string {
584
- return this.showBreadcrumbs ? "" : "margin-top: 48px";
585
- }
586
-
587
668
  private buildBreadcrumbs(): void {
588
669
  const { contentDocumentId } = this.pageReference;
589
670
  if (!contentDocumentId) {
@@ -615,16 +696,30 @@ export default class DocXmlContent extends LightningElementWithState<{
615
696
  return [item];
616
697
  }
617
698
 
699
+ // This method take docId and drops the version from the docId.
700
+ // Example:
701
+ // Takes input string: docId = "atlas.en-us.238.0.b2b_b2c_comm_dev.meta"
702
+ // Output string: filteredDocId = "atlas.en-us.b2b_b2c_comm_dev.meta"
703
+ dropVersionFromDocId(docId: string): string {
704
+ if (!this.version?.id) {
705
+ return docId;
706
+ }
707
+
708
+ const curVersion = this.version.id + ".";
709
+ const filteredDocId = docId.replace(curVersion, "");
710
+ return filteredDocId;
711
+ }
712
+
618
713
  addMetatags(): void {
619
714
  const div = document.createElement("div");
620
- div.innerHTML = this.docContent;
715
+ div.innerHTML = DOMPurify.sanitize(this.docContent);
621
716
  const docDescription = div.querySelector(".shortdesc")?.textContent;
622
717
  const topicTitle = div.querySelector("h1")?.textContent;
623
718
 
624
719
  const title = document.querySelector("title");
625
720
  const composedTitle = this.getComposedTitle(topicTitle, this.docTitle);
626
721
 
627
- if (title) {
722
+ if (title && title.textContent) {
628
723
  title.textContent = composedTitle;
629
724
  }
630
725
  const metatitle = document.querySelector('meta[name="title"]');
@@ -646,14 +741,52 @@ export default class DocXmlContent extends LightningElementWithState<{
646
741
  'link[rel="canonical"]'
647
742
  );
648
743
  if (metadescription) {
744
+ const copyPageReference = { ...this.pageReference };
745
+ copyPageReference.docId = copyPageReference.docId
746
+ ? this.dropVersionFromDocId(copyPageReference.docId)
747
+ : copyPageReference.docId;
649
748
  metadescription.setAttribute(
650
749
  "href",
651
750
  window.location.protocol +
652
751
  "//" +
653
752
  window.location.host +
654
- this.pageReferenceToString(this.pageReference)
753
+ this.pageReferenceToString(copyPageReference)
655
754
  );
656
755
  }
657
756
  }
757
+
758
+ this.addNoIndexMetaForOlderDocVersions();
759
+ }
760
+
761
+ /**
762
+ * Method adds noindex, follow meta tag to the older Couch DB doc pages.
763
+ * Fixes W-12547462.
764
+ */
765
+ private addNoIndexMetaForOlderDocVersions() {
766
+ // eslint-disable-next-line @lwc/lwc/no-document-query
767
+ const headTag = document.getElementsByTagName("head");
768
+ // this checks if the selected version is not the latest version,
769
+ // then it adds the noindex, follow meta tag to the older version pages.
770
+ const versionId = this.version!.id;
771
+ const docId = this.pageReference.docId;
772
+
773
+ // SEO fix:
774
+ // Doc id without version id is always considered latest and should be used for SEO.
775
+ // Condition is to find a docId which includes version id,
776
+ // these docs are always considered as old and should not be indexed including the preview docs.
777
+ if (
778
+ headTag.length &&
779
+ docId?.includes(versionId) &&
780
+ !document.querySelector('meta[name="robots"]')
781
+ ) {
782
+ const robotsMeta = document.createElement("meta");
783
+ robotsMeta.setAttribute("name", "robots");
784
+ robotsMeta.setAttribute("content", "noindex, follow");
785
+ headTag[0].appendChild(robotsMeta);
786
+ }
787
+ }
788
+
789
+ private get showVersionPicker(): boolean {
790
+ return !this.disableVersion;
658
791
  }
659
792
  }
@@ -166,12 +166,12 @@ api-security-documentation {
166
166
  --anypoint-button-emphasis-high-active-background-color: transparent;
167
167
 
168
168
  /* api example */
169
- --api-example-accent-color: rgb(250, 250, 250);
170
- --api-example-background-color: rgb(250, 250, 250);
169
+ --api-example-accent-color: rgb(250 250 250);
170
+ --api-example-background-color: rgb(250 250 250);
171
171
  --api-example-title-background-color: transparent;
172
172
 
173
173
  /* code snippets */
174
- --http-code-snippet-container-background-color: rgb(250, 250, 250);
174
+ --http-code-snippet-container-background-color: rgb(250 250 250);
175
175
  --http-code-snippet-container-padding: var(--dx-g-spacing-md);
176
176
 
177
177
  /* prism */
@@ -208,7 +208,7 @@ api-endpoint-documentation {
208
208
  );
209
209
 
210
210
  /* description */
211
- --api-endpoint-documentation-description-color: rgb(24, 24, 24);
211
+ --api-endpoint-documentation-description-color: rgb(24 24 24);
212
212
  }
213
213
 
214
214
  api-endpoint-documentation,
@@ -271,11 +271,11 @@ api-method-documentation {
271
271
  --api-method-documentation-http-method-label-font-weight: var(
272
272
  --dx-g-font-bold
273
273
  );
274
- --api-method-documentation-description-color: rgb(24, 24, 24);
274
+ --api-method-documentation-description-color: rgb(24 24 24);
275
275
  --api-method-documentation-operation-id-color: var(--dx-g-gray-50);
276
276
 
277
277
  /* body */
278
- --api-body-document-description-color: rgb(24, 24, 24);
278
+ --api-body-document-description-color: rgb(24 24 24);
279
279
  --api-body-document-media-type-selector-color: var(--dx-g-blue-vibrant-20);
280
280
  --api-body-document-media-type-selector-font-size: var(
281
281
  --amf-h8-mod-font-size