@salesforcedevs/docs-components 0.0.0 → 0.0.1-superscript
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lwc.config.json +12 -2
- package/package.json +29 -27
- package/src/modules/doc/{amfReference/utils.ts → amfModelParser/amfModelParser.ts} +10 -5
- package/src/modules/doc/amfReference/amfReference.css +11 -3
- package/src/modules/doc/amfReference/amfReference.html +35 -26
- package/src/modules/doc/amfReference/amfReference.ts +239 -95
- package/src/modules/doc/amfReference/types.ts +3 -11
- package/src/modules/doc/amfTopic/amfTopic.css +20 -0
- package/src/modules/doc/amfTopic/amfTopic.ts +59 -18
- package/src/modules/doc/amfTopic/types.ts +15 -13
- package/src/modules/doc/amfTopic/utils.ts +12 -6
- package/src/modules/doc/breadcrumbItem/breadcrumbItem.ts +17 -10
- package/src/modules/doc/breadcrumbs/breadcrumbs.html +6 -9
- package/src/modules/doc/breadcrumbs/breadcrumbs.ts +30 -34
- package/src/modules/doc/componentPlayground/componentPlayground.css +30 -0
- package/src/modules/doc/componentPlayground/componentPlayground.html +20 -0
- package/src/modules/doc/componentPlayground/componentPlayground.ts +97 -0
- package/src/modules/doc/content/content.css +70 -76
- package/src/modules/doc/content/content.html +1 -0
- package/src/modules/doc/content/content.ts +24 -46
- package/src/modules/doc/contentCallout/contentCallout.css +11 -7
- package/src/modules/doc/contentCallout/contentCallout.html +11 -4
- package/src/modules/doc/contentCallout/contentCallout.ts +8 -1
- package/src/modules/doc/contentLayout/contentLayout.css +1 -98
- package/src/modules/doc/contentLayout/contentLayout.html +46 -31
- package/src/modules/doc/contentLayout/contentLayout.ts +337 -89
- package/src/modules/doc/contentMedia/contentMedia.css +1 -1
- package/src/modules/doc/doDont/doDont.css +47 -0
- package/src/modules/doc/doDont/doDont.html +27 -0
- package/src/modules/doc/doDont/doDont.ts +17 -0
- package/src/modules/doc/header/header.css +65 -36
- package/src/modules/doc/header/header.html +44 -138
- package/src/modules/doc/header/header.ts +62 -74
- package/src/modules/doc/heading/heading.css +16 -37
- package/src/modules/doc/heading/heading.html +4 -4
- package/src/modules/doc/heading/heading.ts +12 -10
- package/src/modules/doc/headingAnchor/headingAnchor.css +2 -2
- package/src/modules/doc/headingAnchor/headingAnchor.ts +2 -2
- package/src/modules/doc/headingContent/headingContent.css +1 -1
- package/src/modules/doc/headingContent/headingContent.html +2 -2
- package/src/modules/doc/headingContent/headingContent.ts +2 -2
- package/src/modules/doc/lwcContentLayout/lwcContentLayout.css +9 -0
- package/src/modules/doc/lwcContentLayout/lwcContentLayout.html +64 -0
- package/src/modules/doc/lwcContentLayout/lwcContentLayout.ts +269 -0
- package/src/modules/doc/overview/overview.css +40 -0
- package/src/modules/doc/overview/overview.html +34 -0
- package/src/modules/doc/overview/overview.ts +12 -0
- package/src/modules/doc/phase/phase.css +11 -3
- package/src/modules/doc/phase/phase.html +12 -2
- package/src/modules/doc/phase/phase.ts +44 -8
- package/src/modules/doc/redocReference/redocReference.css +7 -0
- package/src/modules/doc/redocReference/redocReference.html +13 -0
- package/src/modules/doc/redocReference/redocReference.ts +425 -0
- package/src/modules/doc/specificationContent/specificationContent.css +36 -0
- package/src/modules/doc/specificationContent/specificationContent.html +177 -0
- package/src/modules/doc/specificationContent/specificationContent.ts +166 -0
- package/src/modules/doc/sprigSurvey/sprigSurvey.html +20 -0
- package/src/modules/doc/sprigSurvey/sprigSurvey.scoped.css +16 -0
- package/src/modules/doc/sprigSurvey/sprigSurvey.ts +16 -0
- package/src/modules/doc/superscriptSubscript/superscriptSubscript.html +8 -0
- package/src/modules/doc/superscriptSubscript/superscriptSubscript.ts +16 -0
- package/src/modules/doc/toc/toc.html +1 -3
- package/src/modules/doc/toc/toc.ts +1 -1
- package/src/modules/doc/toolbar/toolbar.ts +6 -6
- package/src/modules/doc/versionPicker/versionPicker.css +64 -0
- package/src/modules/doc/versionPicker/versionPicker.html +40 -0
- package/src/modules/doc/versionPicker/versionPicker.ts +65 -0
- package/src/modules/doc/xmlContent/types.ts +9 -3
- package/src/modules/doc/xmlContent/utils.ts +3 -1
- package/src/modules/doc/xmlContent/xmlContent.css +16 -4
- package/src/modules/doc/xmlContent/xmlContent.html +36 -21
- package/src/modules/doc/xmlContent/xmlContent.ts +295 -120
- package/src/modules/docHelpers/amfStyle/amfStyle.css +6 -8
- package/src/modules/docHelpers/contentLayoutStyle/contentLayoutStyle.css +162 -0
- package/src/modules/docHelpers/imgStyle/imgStyle.css +59 -0
- package/src/modules/docHelpers/status/status.css +1 -1
- package/src/modules/docUtils/{SearchSyncer/SearchSyncer.ts → searchSyncer/searchSyncer.ts} +1 -0
- package/src/modules/docUtils/utils/__mocks__/coveo.analytics.ts +16 -0
- package/src/modules/docUtils/utils/coveo.analytics.d.ts +10 -0
- package/src/modules/docUtils/utils/utils.ts +32 -0
- package/LICENSE +0 -12
- package/src/modules/docBaseElements/lightningElementWithState/lightningElementWithState.ts +0 -93
- package/src/modules/docHelpers/phaseContentLayout/phaseContentLayout.css +0 -39
|
@@ -1,36 +1,47 @@
|
|
|
1
|
+
/* eslint-disable @lwc/lwc/no-document-query */
|
|
1
2
|
import { api, track } from "lwc";
|
|
2
|
-
import { normalizeBoolean } from "dxUtils/normalizers";
|
|
3
3
|
import { FetchContent } from "./utils";
|
|
4
4
|
import {
|
|
5
|
-
CoveoAdvancedQueryXMLConfig,
|
|
6
5
|
DocLanguage,
|
|
7
6
|
DocVersion,
|
|
8
7
|
TreeNode,
|
|
9
8
|
Header,
|
|
9
|
+
SiderbarFooter,
|
|
10
10
|
HistoryState,
|
|
11
11
|
PageReference,
|
|
12
|
-
TocMap
|
|
12
|
+
TocMap,
|
|
13
|
+
ContentData
|
|
13
14
|
} from "./types";
|
|
14
|
-
import { SearchSyncer } from "docUtils/
|
|
15
|
-
import { LightningElementWithState } from "
|
|
16
|
-
import {
|
|
15
|
+
import { SearchSyncer } from "docUtils/searchSyncer";
|
|
16
|
+
import { LightningElementWithState } from "dxBaseElements/lightningElementWithState";
|
|
17
|
+
import { oldVersionDocInfo } from "docUtils/utils";
|
|
18
|
+
import { Breadcrumb, DocPhaseInfo, Language } from "typings/custom";
|
|
19
|
+
import { track as trackGTM } from "dxUtils/analytics";
|
|
20
|
+
import DOMPurify from "dompurify";
|
|
17
21
|
|
|
18
22
|
// 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);
|
|
23
|
+
const handleContentError = (error: any): void => console.log(error);
|
|
20
24
|
|
|
21
25
|
const PIXEL_PER_CHARACTER_MAP: { [key: string]: number } = {
|
|
22
26
|
default: 7.7,
|
|
23
27
|
"ja-jp": 12.5
|
|
24
28
|
};
|
|
25
29
|
|
|
30
|
+
const defaultSidebarFooter: SiderbarFooter = {
|
|
31
|
+
bailHref: "",
|
|
32
|
+
bailLabel: "",
|
|
33
|
+
languages: [],
|
|
34
|
+
language: ""
|
|
35
|
+
};
|
|
26
36
|
export default class DocXmlContent extends LightningElementWithState<{
|
|
27
37
|
isFetchingDocument: boolean;
|
|
28
38
|
isFetchingContent: boolean;
|
|
29
39
|
lastHighlightedSearch: string;
|
|
40
|
+
internalLinkClicked: boolean;
|
|
30
41
|
}> {
|
|
31
42
|
@api apiDomain = "https://developer.salesforce.com";
|
|
32
|
-
@api
|
|
33
|
-
@api
|
|
43
|
+
@api hideFooter = false;
|
|
44
|
+
@api displayReadingTime = false;
|
|
34
45
|
|
|
35
46
|
@api
|
|
36
47
|
get allLanguages(): Array<Language> {
|
|
@@ -43,31 +54,32 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
43
54
|
}
|
|
44
55
|
}
|
|
45
56
|
|
|
46
|
-
@api
|
|
47
|
-
get enableCoveo() {
|
|
48
|
-
return this._enableCoveo;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
set enableCoveo(value) {
|
|
52
|
-
this._enableCoveo = normalizeBoolean(value);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
57
|
private availableLanguages: Array<DocLanguage> = [];
|
|
56
|
-
private availableVersions: Array<DocVersion> = [];
|
|
57
|
-
private contentProvider
|
|
58
|
+
@track private availableVersions: Array<DocVersion> = [];
|
|
59
|
+
private contentProvider?: FetchContent;
|
|
58
60
|
private docContent = "";
|
|
59
|
-
private language
|
|
60
|
-
private
|
|
61
|
+
private language?: DocLanguage | null = null;
|
|
62
|
+
private displayContent = false;
|
|
63
|
+
private display404 = false;
|
|
64
|
+
private _pageHeader?: Header;
|
|
61
65
|
private pdfUrl = "";
|
|
62
66
|
private tocMap: TocMap = {};
|
|
63
|
-
private sidebarContent: Array<TreeNode> = null;
|
|
64
|
-
private version: DocVersion = null;
|
|
67
|
+
private sidebarContent: Array<TreeNode> | null = null;
|
|
68
|
+
private version: DocVersion | null = null;
|
|
65
69
|
private docTitle = "";
|
|
66
|
-
private analyticsEvent = "custEv_ctaLinkClick";
|
|
67
70
|
private _pathName = "";
|
|
68
|
-
private _pageHeader?: Header;
|
|
69
71
|
private listenerAttached = false;
|
|
70
|
-
private
|
|
72
|
+
private sidebarFooterContent: SiderbarFooter = { ...defaultSidebarFooter };
|
|
73
|
+
private latestVersion = false;
|
|
74
|
+
private previewVersion = false;
|
|
75
|
+
|
|
76
|
+
private get enableFooter(): boolean {
|
|
77
|
+
return !this.hideFooter;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private get computedReadingTime(): number | undefined {
|
|
81
|
+
return this.displayReadingTime ? this.readingTime : undefined;
|
|
82
|
+
}
|
|
71
83
|
|
|
72
84
|
private searchSyncer = new SearchSyncer({
|
|
73
85
|
callbacks: {
|
|
@@ -107,8 +119,40 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
107
119
|
});
|
|
108
120
|
private _allLanguages: Array<Language> = [];
|
|
109
121
|
|
|
122
|
+
private get oldVersionInfo(): DocPhaseInfo | null {
|
|
123
|
+
let info = null;
|
|
124
|
+
if (!this.disableVersion) {
|
|
125
|
+
const currentGAVersion = this.versionOptions.find(
|
|
126
|
+
(version) => !version.url.includes(version.id)
|
|
127
|
+
);
|
|
128
|
+
if (currentGAVersion?.link?.href && this.version?.id) {
|
|
129
|
+
const versionNo = currentGAVersion.id;
|
|
130
|
+
/**
|
|
131
|
+
* Need to show old version doc banner only if the version is less than the current ga version
|
|
132
|
+
* We should not show it to the preview version whose version is more than ga
|
|
133
|
+
**/
|
|
134
|
+
try {
|
|
135
|
+
if (parseFloat(this.version.id) < parseFloat(versionNo)) {
|
|
136
|
+
info = oldVersionDocInfo(currentGAVersion.link.href);
|
|
137
|
+
} else if (
|
|
138
|
+
parseFloat(this.version.id) > parseFloat(versionNo)
|
|
139
|
+
) {
|
|
140
|
+
this.previewVersion = true;
|
|
141
|
+
}
|
|
142
|
+
} catch (exception) {
|
|
143
|
+
/* Ideally this use case should not happen, but just added to not to break the page*/
|
|
144
|
+
console.warn(exception);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return info;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@track showVersionBanner = false;
|
|
152
|
+
|
|
110
153
|
@track private pageReference: PageReference = {};
|
|
111
154
|
@track breadcrumbs: Array<Breadcrumb> = [];
|
|
155
|
+
@track readingTime?: number;
|
|
112
156
|
|
|
113
157
|
constructor() {
|
|
114
158
|
super();
|
|
@@ -133,7 +177,13 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
133
177
|
this.apiDomain,
|
|
134
178
|
this.allLanguages
|
|
135
179
|
);
|
|
136
|
-
this.fetchDocument().then(() =>
|
|
180
|
+
this.fetchDocument().then((content: any) => {
|
|
181
|
+
if (content) {
|
|
182
|
+
this.displayContent = true;
|
|
183
|
+
} else {
|
|
184
|
+
this.display404 = true;
|
|
185
|
+
}
|
|
186
|
+
});
|
|
137
187
|
window.addEventListener("popstate", this.handlePopState);
|
|
138
188
|
|
|
139
189
|
this.searchSyncer.init();
|
|
@@ -142,8 +192,8 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
142
192
|
renderedCallback(): void {
|
|
143
193
|
this.setState({ internalLinkClicked: true });
|
|
144
194
|
const urlSectionLink =
|
|
145
|
-
this.pageReference?.hash?.split("#").length > 1
|
|
146
|
-
? this.pageReference.hash
|
|
195
|
+
this.pageReference?.hash?.split("#").length! > 1
|
|
196
|
+
? this.pageReference.hash!.split("#")[1]
|
|
147
197
|
: this.pageReference?.hash;
|
|
148
198
|
|
|
149
199
|
const contentEl = this.template.querySelector("doc-content");
|
|
@@ -156,6 +206,7 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
156
206
|
|
|
157
207
|
if (anchorEl) {
|
|
158
208
|
anchorEl.scrollIntoView();
|
|
209
|
+
|
|
159
210
|
this.setState({ internalLinkClicked: false });
|
|
160
211
|
}
|
|
161
212
|
|
|
@@ -181,24 +232,17 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
181
232
|
disconnectedCallback(): void {
|
|
182
233
|
window.removeEventListener("popstate", this.handlePopState);
|
|
183
234
|
this.searchSyncer.dispose();
|
|
184
|
-
if (this.listenerAttached) {
|
|
185
|
-
this.pageHeader.removeEventListener(
|
|
186
|
-
"langchange",
|
|
187
|
-
this.handleLanguageChange
|
|
188
|
-
);
|
|
189
|
-
this.listenerAttached = false;
|
|
190
|
-
}
|
|
191
235
|
}
|
|
192
236
|
|
|
193
|
-
private get languageId(): string {
|
|
194
|
-
return this.language
|
|
237
|
+
private get languageId(): string | undefined {
|
|
238
|
+
return this.language?.id.replace("-", "_");
|
|
195
239
|
}
|
|
196
240
|
|
|
197
|
-
private get releaseVersionId(): string {
|
|
198
|
-
return this.version
|
|
241
|
+
private get releaseVersionId(): string | undefined {
|
|
242
|
+
return this.version?.id;
|
|
199
243
|
}
|
|
200
244
|
|
|
201
|
-
private get deliverable(): string {
|
|
245
|
+
private get deliverable(): string | undefined {
|
|
202
246
|
return this.pageReference.deliverable;
|
|
203
247
|
}
|
|
204
248
|
|
|
@@ -206,34 +250,16 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
206
250
|
// Coveo is enabled and the version is greater than 51 (within the latest 3 versions)
|
|
207
251
|
// TODO: we need a better fix for version number check
|
|
208
252
|
return !(
|
|
209
|
-
this.
|
|
210
|
-
this.
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
(this.version?.releaseVersion &&
|
|
214
|
-
parseInt(
|
|
215
|
-
this.version.releaseVersion.replace("v", ""),
|
|
216
|
-
10
|
|
217
|
-
) >= 53))
|
|
253
|
+
!this.version?.releaseVersion ||
|
|
254
|
+
(this.version?.releaseVersion &&
|
|
255
|
+
parseInt(this.version.releaseVersion.replace("v", ""), 10) >=
|
|
256
|
+
53)
|
|
218
257
|
);
|
|
219
258
|
}
|
|
220
259
|
|
|
221
|
-
private get coveoAdvancedQueryConfig(): CoveoAdvancedQueryXMLConfig {
|
|
222
|
-
const config: { locale: string; topicid: string; version?: string } = {
|
|
223
|
-
locale: this.languageId,
|
|
224
|
-
topicid: this.deliverable
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
if (this.releaseVersionId && this.releaseVersionId !== "noversion") {
|
|
228
|
-
config.version = this.releaseVersionId;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return config;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
260
|
private get pageHeader(): Header {
|
|
235
261
|
if (!this._pageHeader) {
|
|
236
|
-
this._pageHeader = document.querySelector("doc-header")
|
|
262
|
+
this._pageHeader = document.querySelector("doc-header")!;
|
|
237
263
|
}
|
|
238
264
|
|
|
239
265
|
return this._pageHeader;
|
|
@@ -276,13 +302,24 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
276
302
|
|
|
277
303
|
private get breadcrumbPixelPerCharacter() {
|
|
278
304
|
return (
|
|
279
|
-
PIXEL_PER_CHARACTER_MAP[this.language
|
|
305
|
+
PIXEL_PER_CHARACTER_MAP[this.language!.id] ||
|
|
280
306
|
PIXEL_PER_CHARACTER_MAP.default
|
|
281
307
|
);
|
|
282
308
|
}
|
|
283
309
|
|
|
284
|
-
private
|
|
285
|
-
|
|
310
|
+
private get ANALYTICS_PAYLOAD() {
|
|
311
|
+
return {
|
|
312
|
+
element_title: "version picker",
|
|
313
|
+
content_category: "cta"
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
private handlePopState = (event: PopStateEvent): void =>
|
|
318
|
+
this.updatePageReference(this.getReferenceFromUrl(), event);
|
|
319
|
+
|
|
320
|
+
handleDismissVersionBanner() {
|
|
321
|
+
this.showVersionBanner = false;
|
|
322
|
+
}
|
|
286
323
|
|
|
287
324
|
handleSelect(event: CustomEvent<{ name: string }>): void {
|
|
288
325
|
event.stopPropagation();
|
|
@@ -314,7 +351,7 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
314
351
|
this.updateUrl();
|
|
315
352
|
}
|
|
316
353
|
|
|
317
|
-
handleLanguageChange = (event:
|
|
354
|
+
handleLanguageChange = (event: any) => {
|
|
318
355
|
if (this.language && this.language.id === event.detail) {
|
|
319
356
|
return;
|
|
320
357
|
}
|
|
@@ -322,12 +359,26 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
322
359
|
this.language = this.availableLanguages.find(
|
|
323
360
|
({ id }) => id === event.detail
|
|
324
361
|
);
|
|
325
|
-
this.pageReference.docId = this.language
|
|
362
|
+
this.pageReference.docId = this.language!.url;
|
|
363
|
+
|
|
364
|
+
trackGTM(event.target!, "custEv_linkClick", {
|
|
365
|
+
click_text: event.detail,
|
|
366
|
+
element_title: "language selector",
|
|
367
|
+
click_url: `${window.location.origin}${this.pageReferenceToString(
|
|
368
|
+
this.pageReference
|
|
369
|
+
)}`,
|
|
370
|
+
element_type: "link",
|
|
371
|
+
content_category: "cta"
|
|
372
|
+
});
|
|
373
|
+
|
|
326
374
|
this.updateUrl();
|
|
327
375
|
this.fetchDocument();
|
|
328
376
|
};
|
|
329
377
|
|
|
330
|
-
updatePageReference(
|
|
378
|
+
updatePageReference(
|
|
379
|
+
newPageReference: PageReference,
|
|
380
|
+
event: PopStateEvent | undefined = undefined
|
|
381
|
+
): void {
|
|
331
382
|
this.pageReference.hash = newPageReference.hash;
|
|
332
383
|
this.pageReference.search = newPageReference.search;
|
|
333
384
|
|
|
@@ -344,13 +395,26 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
344
395
|
}
|
|
345
396
|
|
|
346
397
|
this.fetchContent()
|
|
347
|
-
.then(() =>
|
|
398
|
+
.then(() => {
|
|
399
|
+
this.buildBreadcrumbs();
|
|
400
|
+
document.body.scrollTop = event?.state?.scroll?.value || 0;
|
|
401
|
+
})
|
|
348
402
|
.catch(handleContentError);
|
|
349
403
|
}
|
|
350
404
|
|
|
405
|
+
private sanitizeUrlPart(part: string | undefined): string | undefined {
|
|
406
|
+
if (!part) {
|
|
407
|
+
return part;
|
|
408
|
+
}
|
|
409
|
+
return DOMPurify.sanitize(part);
|
|
410
|
+
}
|
|
411
|
+
|
|
351
412
|
getReferenceFromUrl(): PageReference {
|
|
352
413
|
const [page, docId, deliverable, contentDocumentId] =
|
|
353
|
-
window.location.pathname
|
|
414
|
+
window.location.pathname
|
|
415
|
+
.substr(1)
|
|
416
|
+
.split("/")
|
|
417
|
+
.map(this.sanitizeUrlPart);
|
|
354
418
|
|
|
355
419
|
const { origin: domain, hash, search } = window.location;
|
|
356
420
|
|
|
@@ -359,9 +423,9 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
359
423
|
deliverable,
|
|
360
424
|
docId,
|
|
361
425
|
domain,
|
|
362
|
-
hash,
|
|
426
|
+
hash: this.sanitizeUrlPart(hash),
|
|
363
427
|
page,
|
|
364
|
-
search
|
|
428
|
+
search: this.sanitizeUrlPart(search)
|
|
365
429
|
};
|
|
366
430
|
}
|
|
367
431
|
|
|
@@ -375,12 +439,13 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
375
439
|
);
|
|
376
440
|
}
|
|
377
441
|
|
|
378
|
-
async fetchDocument(): Promise<
|
|
442
|
+
async fetchDocument(): Promise<string> {
|
|
379
443
|
this.setState({
|
|
380
444
|
isFetchingDocument: true
|
|
381
445
|
});
|
|
382
|
-
|
|
383
|
-
|
|
446
|
+
|
|
447
|
+
const data = await this.contentProvider!.fetchDocumentData(
|
|
448
|
+
this.pageReference.docId!
|
|
384
449
|
);
|
|
385
450
|
|
|
386
451
|
// This could be a 404 scenario.
|
|
@@ -388,7 +453,7 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
388
453
|
this.setState({
|
|
389
454
|
isFetchingDocument: false
|
|
390
455
|
});
|
|
391
|
-
return;
|
|
456
|
+
return "";
|
|
392
457
|
}
|
|
393
458
|
|
|
394
459
|
this.docTitle = data.docTitle;
|
|
@@ -400,7 +465,7 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
400
465
|
this.availableVersions = data.availableVersions;
|
|
401
466
|
this.pdfUrl = data.pdfUrl;
|
|
402
467
|
|
|
403
|
-
this.
|
|
468
|
+
this.updateHeaderAndSidebarFooter();
|
|
404
469
|
|
|
405
470
|
this.buildBreadcrumbs();
|
|
406
471
|
|
|
@@ -409,16 +474,22 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
409
474
|
this.updateUrl(HistoryState.REPLACE_STATE);
|
|
410
475
|
}
|
|
411
476
|
|
|
477
|
+
if (this.oldVersionInfo) {
|
|
478
|
+
this.showVersionBanner = true;
|
|
479
|
+
} else {
|
|
480
|
+
this.latestVersion = true;
|
|
481
|
+
}
|
|
482
|
+
|
|
412
483
|
if (
|
|
413
484
|
this.pageReference?.contentDocumentId?.replace(/\.htm$/, "") !==
|
|
414
485
|
data.id
|
|
415
486
|
) {
|
|
416
487
|
try {
|
|
417
|
-
await this.fetchContent();
|
|
488
|
+
const moreData = await this.fetchContent();
|
|
418
489
|
this.setState({
|
|
419
490
|
isFetchingDocument: false
|
|
420
491
|
});
|
|
421
|
-
return;
|
|
492
|
+
return moreData.content;
|
|
422
493
|
} catch (error) {
|
|
423
494
|
this.pageReference.contentDocumentId = `${data.id}.htm`;
|
|
424
495
|
this.pageReference.hash = "";
|
|
@@ -428,43 +499,69 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
428
499
|
}
|
|
429
500
|
|
|
430
501
|
this.docContent = data.content;
|
|
502
|
+
this.calculateReadingTime(data.content);
|
|
431
503
|
this.addMetatags();
|
|
432
504
|
this.setState({
|
|
433
505
|
isFetchingDocument: false
|
|
434
506
|
});
|
|
507
|
+
|
|
508
|
+
// Track page view when a new document is loaded
|
|
509
|
+
trackGTM(this.template.host, "doc_page_view", {
|
|
510
|
+
page_path: this.pageReferenceToString(this.pageReference),
|
|
511
|
+
doc_id: this.pageReference.docId,
|
|
512
|
+
content_document_id: this.pageReference.contentDocumentId,
|
|
513
|
+
deliverable: this.pageReference.deliverable,
|
|
514
|
+
language: this.language?.id,
|
|
515
|
+
version: this.version?.id,
|
|
516
|
+
article_name: this.getArticleName()
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
return data.content;
|
|
435
520
|
}
|
|
436
521
|
|
|
437
|
-
async fetchContent(): Promise<
|
|
522
|
+
async fetchContent(): Promise<ContentData> {
|
|
438
523
|
this.setState({
|
|
439
524
|
isFetchingContent: true
|
|
440
525
|
});
|
|
441
|
-
const data = await this.contentProvider
|
|
442
|
-
this.pageReference.deliverable
|
|
443
|
-
this.pageReference.contentDocumentId
|
|
526
|
+
const data = await this.contentProvider!.fetchContent(
|
|
527
|
+
this.pageReference.deliverable!,
|
|
528
|
+
this.pageReference.contentDocumentId!,
|
|
444
529
|
{
|
|
445
|
-
language: this.language
|
|
446
|
-
version: this.version
|
|
530
|
+
language: this.language!.id,
|
|
531
|
+
version: this.version!.id
|
|
447
532
|
}
|
|
448
533
|
);
|
|
449
534
|
|
|
450
535
|
if (data) {
|
|
451
536
|
this.docContent = data.content;
|
|
537
|
+
this.calculateReadingTime(data.content);
|
|
452
538
|
this.addMetatags();
|
|
453
539
|
|
|
454
540
|
if (!this.pageReference.hash) {
|
|
455
|
-
document.
|
|
456
|
-
behavior: "smooth",
|
|
457
|
-
block: "start",
|
|
458
|
-
inline: "nearest"
|
|
459
|
-
});
|
|
541
|
+
document.body.scrollIntoView();
|
|
460
542
|
}
|
|
543
|
+
|
|
544
|
+
// Track page view when new content is loaded (navigation within same document)
|
|
545
|
+
// Note: breadcrumbs will be built after this in updatePageReference,
|
|
546
|
+
// but we need to build them here to get article_name
|
|
547
|
+
this.buildBreadcrumbs();
|
|
548
|
+
trackGTM(this.template.host, "doc_page_view", {
|
|
549
|
+
page_path: this.pageReferenceToString(this.pageReference),
|
|
550
|
+
doc_id: this.pageReference.docId,
|
|
551
|
+
content_document_id: this.pageReference.contentDocumentId,
|
|
552
|
+
deliverable: this.pageReference.deliverable,
|
|
553
|
+
language: this.language?.id,
|
|
554
|
+
version: this.version?.id,
|
|
555
|
+
article_name: this.getArticleName()
|
|
556
|
+
});
|
|
461
557
|
}
|
|
462
558
|
this.setState({
|
|
463
559
|
isFetchingContent: false
|
|
464
560
|
});
|
|
561
|
+
return data;
|
|
465
562
|
}
|
|
466
563
|
|
|
467
|
-
|
|
564
|
+
updateHeaderAndSidebarFooter(): void {
|
|
468
565
|
if (!this.pageHeader) {
|
|
469
566
|
return;
|
|
470
567
|
}
|
|
@@ -474,20 +571,12 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
474
571
|
}
|
|
475
572
|
|
|
476
573
|
if (this.pdfUrl) {
|
|
477
|
-
this.
|
|
478
|
-
this.
|
|
574
|
+
this.sidebarFooterContent.bailHref = this.pdfUrl;
|
|
575
|
+
this.sidebarFooterContent.bailLabel = "PDF";
|
|
479
576
|
}
|
|
480
577
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
"langchange",
|
|
484
|
-
this.handleLanguageChange
|
|
485
|
-
);
|
|
486
|
-
this.listenerAttached = true;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
this.pageHeader.languages = this.availableLanguages;
|
|
490
|
-
this.pageHeader.language = this.language?.id;
|
|
578
|
+
this.sidebarFooterContent.languages = this.availableLanguages;
|
|
579
|
+
this.sidebarFooterContent.language = this.language?.id;
|
|
491
580
|
|
|
492
581
|
if (this.pageReference) {
|
|
493
582
|
const { docId, deliverable, page } = this.pageReference;
|
|
@@ -511,23 +600,24 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
511
600
|
}
|
|
512
601
|
|
|
513
602
|
private updateSearchInput(searchParam: string): void {
|
|
514
|
-
this.
|
|
515
|
-
.querySelector("doc-content-layout")
|
|
516
|
-
?.setSidebarInputValue(searchParam);
|
|
603
|
+
(this.refs.docContentLayout as any)?.setSidebarInputValue(searchParam);
|
|
517
604
|
}
|
|
518
605
|
|
|
519
606
|
private pageReferenceToString(reference: PageReference): string {
|
|
520
607
|
const { page, docId, deliverable, contentDocumentId, hash, search } =
|
|
521
608
|
reference;
|
|
522
609
|
return `/${page}/${docId}/${deliverable}/${contentDocumentId}${this.normalizeSearch(
|
|
523
|
-
search
|
|
610
|
+
search!
|
|
524
611
|
)}${this.normalizeHash(hash)}`;
|
|
525
612
|
}
|
|
526
613
|
|
|
527
|
-
private normalizeUrlPart(
|
|
614
|
+
private normalizeUrlPart(
|
|
615
|
+
part: string | undefined,
|
|
616
|
+
sentinel: string
|
|
617
|
+
): string {
|
|
528
618
|
return (
|
|
529
619
|
(part &&
|
|
530
|
-
(part.startsWith(sentinel) ? part : `${sentinel}${part}`)) ||
|
|
620
|
+
(part.startsWith(sentinel!) ? part : `${sentinel}${part}`)) ||
|
|
531
621
|
""
|
|
532
622
|
);
|
|
533
623
|
}
|
|
@@ -536,16 +626,16 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
536
626
|
return this.normalizeUrlPart(search, "?");
|
|
537
627
|
}
|
|
538
628
|
|
|
539
|
-
private normalizeHash(hash
|
|
629
|
+
private normalizeHash(hash?: string): string {
|
|
540
630
|
return this.normalizeUrlPart(hash, "#");
|
|
541
631
|
}
|
|
542
632
|
|
|
543
633
|
private getComposedTitle(
|
|
544
|
-
topicTitle: string | undefined,
|
|
634
|
+
topicTitle: string | null | undefined,
|
|
545
635
|
docTitle: string | undefined
|
|
546
636
|
): string {
|
|
547
637
|
// map to avoid duplicates
|
|
548
|
-
const titleMap = {};
|
|
638
|
+
const titleMap: { [key: string]: any } = {};
|
|
549
639
|
if (topicTitle) {
|
|
550
640
|
// 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
641
|
// here it contains \n in the text context which needs to be removed
|
|
@@ -580,8 +670,13 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
580
670
|
return this.breadcrumbs && this.breadcrumbs.length > 1;
|
|
581
671
|
}
|
|
582
672
|
|
|
583
|
-
|
|
584
|
-
|
|
673
|
+
private getArticleName(): string | undefined {
|
|
674
|
+
if (!this.breadcrumbs || this.breadcrumbs.length === 0) {
|
|
675
|
+
return undefined;
|
|
676
|
+
}
|
|
677
|
+
// Join all breadcrumb labels with "/" to create article name
|
|
678
|
+
// e.g., "Tooling API Objects/FieldDefinition"
|
|
679
|
+
return this.breadcrumbs.map((crumb) => crumb.label).join("/");
|
|
585
680
|
}
|
|
586
681
|
|
|
587
682
|
private buildBreadcrumbs(): void {
|
|
@@ -615,16 +710,30 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
615
710
|
return [item];
|
|
616
711
|
}
|
|
617
712
|
|
|
713
|
+
// This method take docId and drops the version from the docId.
|
|
714
|
+
// Example:
|
|
715
|
+
// Takes input string: docId = "atlas.en-us.238.0.b2b_b2c_comm_dev.meta"
|
|
716
|
+
// Output string: filteredDocId = "atlas.en-us.b2b_b2c_comm_dev.meta"
|
|
717
|
+
dropVersionFromDocId(docId: string): string {
|
|
718
|
+
if (!this.version?.id) {
|
|
719
|
+
return docId;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
const curVersion = this.version.id + ".";
|
|
723
|
+
const filteredDocId = docId.replace(curVersion, "");
|
|
724
|
+
return filteredDocId;
|
|
725
|
+
}
|
|
726
|
+
|
|
618
727
|
addMetatags(): void {
|
|
619
728
|
const div = document.createElement("div");
|
|
620
|
-
div.innerHTML = this.docContent;
|
|
729
|
+
div.innerHTML = DOMPurify.sanitize(this.docContent);
|
|
621
730
|
const docDescription = div.querySelector(".shortdesc")?.textContent;
|
|
622
731
|
const topicTitle = div.querySelector("h1")?.textContent;
|
|
623
732
|
|
|
624
733
|
const title = document.querySelector("title");
|
|
625
734
|
const composedTitle = this.getComposedTitle(topicTitle, this.docTitle);
|
|
626
735
|
|
|
627
|
-
if (title) {
|
|
736
|
+
if (title && title.textContent) {
|
|
628
737
|
title.textContent = composedTitle;
|
|
629
738
|
}
|
|
630
739
|
const metatitle = document.querySelector('meta[name="title"]');
|
|
@@ -646,14 +755,80 @@ export default class DocXmlContent extends LightningElementWithState<{
|
|
|
646
755
|
'link[rel="canonical"]'
|
|
647
756
|
);
|
|
648
757
|
if (metadescription) {
|
|
758
|
+
const copyPageReference = { ...this.pageReference };
|
|
759
|
+
copyPageReference.docId = copyPageReference.docId
|
|
760
|
+
? this.dropVersionFromDocId(copyPageReference.docId)
|
|
761
|
+
: copyPageReference.docId;
|
|
649
762
|
metadescription.setAttribute(
|
|
650
763
|
"href",
|
|
651
764
|
window.location.protocol +
|
|
652
765
|
"//" +
|
|
653
766
|
window.location.host +
|
|
654
|
-
this.pageReferenceToString(
|
|
767
|
+
this.pageReferenceToString(copyPageReference)
|
|
655
768
|
);
|
|
656
769
|
}
|
|
657
770
|
}
|
|
771
|
+
|
|
772
|
+
this.addNoIndexMetaForOlderDocVersions();
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Method adds noindex, follow meta tag to the older Couch DB doc pages.
|
|
777
|
+
* Fixes W-12547462.
|
|
778
|
+
*/
|
|
779
|
+
private addNoIndexMetaForOlderDocVersions() {
|
|
780
|
+
// eslint-disable-next-line @lwc/lwc/no-document-query
|
|
781
|
+
const headTag = document.getElementsByTagName("head");
|
|
782
|
+
// this checks if the selected version is not the latest version,
|
|
783
|
+
// then it adds the noindex, follow meta tag to the older version pages.
|
|
784
|
+
const versionId = this.version!.id;
|
|
785
|
+
const docId = this.pageReference.docId;
|
|
786
|
+
|
|
787
|
+
// SEO fix:
|
|
788
|
+
// Doc id without version id is always considered latest and should be used for SEO.
|
|
789
|
+
// Condition is to find a docId which includes version id,
|
|
790
|
+
// these docs are always considered as old and should not be indexed including the preview docs.
|
|
791
|
+
if (
|
|
792
|
+
headTag.length &&
|
|
793
|
+
docId?.includes(versionId) &&
|
|
794
|
+
!document.querySelector('meta[name="robots"]')
|
|
795
|
+
) {
|
|
796
|
+
const robotsMeta = document.createElement("meta");
|
|
797
|
+
robotsMeta.setAttribute("name", "robots");
|
|
798
|
+
robotsMeta.setAttribute("content", "noindex, follow");
|
|
799
|
+
headTag[0].appendChild(robotsMeta);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
private get showVersionPicker(): boolean {
|
|
804
|
+
return !this.disableVersion;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
private calculateReadingTime(content: string): void {
|
|
808
|
+
if (!this.displayReadingTime) {
|
|
809
|
+
this.readingTime = undefined;
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
if (!content) {
|
|
814
|
+
this.readingTime = undefined;
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// Strip HTML tags and get text content
|
|
819
|
+
const div = document.createElement("div");
|
|
820
|
+
div.innerHTML = DOMPurify.sanitize(content);
|
|
821
|
+
const textContent = div.textContent || div.innerText || "";
|
|
822
|
+
|
|
823
|
+
// Count words (split by whitespace and filter out empty strings)
|
|
824
|
+
const wordCount = textContent
|
|
825
|
+
.trim()
|
|
826
|
+
.split(/\s+/)
|
|
827
|
+
.filter((word) => word.length > 0).length;
|
|
828
|
+
|
|
829
|
+
// Calculate reading time: (wordCount / 240) + 1, rounded
|
|
830
|
+
const readingTimeMinutes = Math.round(wordCount / 240 + 1);
|
|
831
|
+
this.readingTime =
|
|
832
|
+
readingTimeMinutes > 0 ? readingTimeMinutes : undefined;
|
|
658
833
|
}
|
|
659
834
|
}
|