@salesforcedevs/docs-components 1.3.205 → 1.3.209-alpha1
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 +2 -0
- package/package.json +4 -2
- package/src/modules/doc/atlasContent/atlasContent.css +52 -0
- package/src/modules/doc/atlasContent/atlasContent.html +48 -0
- package/src/modules/doc/atlasContent/atlasContent.ts +116 -0
- package/src/modules/doc/contentCallout/contentCallout.css +5 -0
- package/src/modules/doc/xmlContent/baseContentElement.ts +787 -0
- package/src/modules/doc/xmlContent/types.ts +110 -0
- package/src/modules/doc/xmlContent/utils.ts +1 -1
- package/src/modules/doc/zoominContent/mockResponses.ts +5223 -0
- package/src/modules/doc/zoominContent/zoominContent.css +52 -0
- package/src/modules/doc/zoominContent/zoominContent.html +48 -0
- package/src/modules/doc/zoominContent/zoominContent.ts +476 -0
- package/src/modules/doc/zoominContent/zoominUtils.ts +88 -0
- package/LICENSE +0 -12
|
@@ -0,0 +1,787 @@
|
|
|
1
|
+
/* eslint-disable @lwc/lwc/no-document-query */
|
|
2
|
+
import { api, track } from "lwc";
|
|
3
|
+
import { normalizeBoolean } from "dxUtils/normalizers";
|
|
4
|
+
import { FetchContent } from "./utils";
|
|
5
|
+
import {
|
|
6
|
+
CoveoAdvancedQueryXMLConfig,
|
|
7
|
+
DocLanguage,
|
|
8
|
+
DocVersion,
|
|
9
|
+
TreeNode,
|
|
10
|
+
Header,
|
|
11
|
+
HistoryState,
|
|
12
|
+
PageReference,
|
|
13
|
+
TocMap
|
|
14
|
+
} from "./types";
|
|
15
|
+
import { SearchSyncer } from "docUtils/searchSyncer";
|
|
16
|
+
import { LightningElementWithState } from "dxBaseElements/lightningElementWithState";
|
|
17
|
+
import { logCoveoPageView, oldVersionDocInfo } from "docUtils/utils";
|
|
18
|
+
import { Breadcrumb, DocPhaseInfo, Language } from "typings/custom";
|
|
19
|
+
import { track as trackGTM } from "dxUtils/analytics";
|
|
20
|
+
|
|
21
|
+
// TODO: Imitating from actual implementation as doc-content use it like this. We should refactor it later.
|
|
22
|
+
const handleContentError = (error: any): void => console.log(error);
|
|
23
|
+
|
|
24
|
+
const PIXEL_PER_CHARACTER_MAP: { [key: string]: number } = {
|
|
25
|
+
default: 7.7,
|
|
26
|
+
"ja-jp": 12.5
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export abstract class BaseContentElement extends LightningElementWithState<{
|
|
30
|
+
isFetchingDocument: boolean;
|
|
31
|
+
isFetchingContent: boolean;
|
|
32
|
+
lastHighlightedSearch: string;
|
|
33
|
+
internalLinkClicked: boolean;
|
|
34
|
+
}> {
|
|
35
|
+
@api apiDomain = "https://developer.salesforce.com";
|
|
36
|
+
@api coveoOrganizationId!: string;
|
|
37
|
+
@api coveoPublicAccessToken!: string;
|
|
38
|
+
@api coveoAnalyticsToken!: string;
|
|
39
|
+
@api coveoSearchHub!: string;
|
|
40
|
+
|
|
41
|
+
@api
|
|
42
|
+
get allLanguages(): Array<Language> {
|
|
43
|
+
return this._allLanguages;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
set allLanguages(value: string) {
|
|
47
|
+
if (value) {
|
|
48
|
+
this._allLanguages = JSON.parse(value);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@api
|
|
53
|
+
get enableCoveo() {
|
|
54
|
+
return this._enableCoveo;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
set enableCoveo(value) {
|
|
58
|
+
this._enableCoveo = normalizeBoolean(value);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
availableLanguages: Array<DocLanguage> = [];
|
|
62
|
+
availableVersions: Array<DocVersion> = [];
|
|
63
|
+
contentProvider?: FetchContent;
|
|
64
|
+
docContent = "";
|
|
65
|
+
language?: DocLanguage | null = null;
|
|
66
|
+
private loaded = false;
|
|
67
|
+
pdfUrl = "";
|
|
68
|
+
tocMap: TocMap = {};
|
|
69
|
+
sidebarContent: Array<TreeNode> | null = null;
|
|
70
|
+
version: DocVersion | null = null;
|
|
71
|
+
docTitle = "";
|
|
72
|
+
private _pathName = "";
|
|
73
|
+
private _pageHeader?: Header;
|
|
74
|
+
private listenerAttached = false;
|
|
75
|
+
private _enableCoveo?: boolean = false;
|
|
76
|
+
|
|
77
|
+
private searchSyncer = new SearchSyncer({
|
|
78
|
+
callbacks: {
|
|
79
|
+
onSearchChange: (nextSearchString: string): void => {
|
|
80
|
+
if (nextSearchString !== this.pageReference.search) {
|
|
81
|
+
this.updatePageReference({
|
|
82
|
+
...this.pageReference,
|
|
83
|
+
search: nextSearchString
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
if (nextSearchString !== this.state.lastHighlightedSearch) {
|
|
87
|
+
this.updateHighlighting(
|
|
88
|
+
new URLSearchParams(nextSearchString).get("q") || ""
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
onUrlChange: (nextSearchString: string): void => {
|
|
93
|
+
if (nextSearchString !== this.pageReference.search) {
|
|
94
|
+
this.updatePageReference({
|
|
95
|
+
...this.pageReference,
|
|
96
|
+
search: nextSearchString
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
const nextSearchParam =
|
|
100
|
+
new URLSearchParams(nextSearchString).get("q") || "";
|
|
101
|
+
this.updateSearchInput(nextSearchParam);
|
|
102
|
+
if (nextSearchString !== this.state.lastHighlightedSearch) {
|
|
103
|
+
this.updateHighlighting(nextSearchParam);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
eventName: "sidebarsearchchange",
|
|
108
|
+
historyMethod: window.history.pushState,
|
|
109
|
+
searchParam: "q",
|
|
110
|
+
shouldStopPropagation: true,
|
|
111
|
+
target: window
|
|
112
|
+
});
|
|
113
|
+
private _allLanguages: Array<Language> = [];
|
|
114
|
+
|
|
115
|
+
get oldVersionInfo(): DocPhaseInfo | null {
|
|
116
|
+
let info = null;
|
|
117
|
+
if (!this.disableVersion) {
|
|
118
|
+
const currentGAVersion = this.versionOptions.find(
|
|
119
|
+
(version) => !version.url.includes(version.id)
|
|
120
|
+
);
|
|
121
|
+
if (currentGAVersion?.link?.href && this.version?.id) {
|
|
122
|
+
const versionNo = currentGAVersion.id;
|
|
123
|
+
/**
|
|
124
|
+
* Need to show old version doc banner only if the version is less than the current ga version
|
|
125
|
+
* We should not show it to the preview version whose version is more than ga
|
|
126
|
+
**/
|
|
127
|
+
try {
|
|
128
|
+
if (parseFloat(this.version.id) < parseFloat(versionNo)) {
|
|
129
|
+
info = oldVersionDocInfo(currentGAVersion.link.href);
|
|
130
|
+
}
|
|
131
|
+
} catch (exception) {
|
|
132
|
+
/* Ideally this use case should not happen, but just added to not to break the page*/
|
|
133
|
+
console.warn(exception);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return info;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@track showVersionBanner = false;
|
|
141
|
+
|
|
142
|
+
@track pageReference: PageReference = {};
|
|
143
|
+
@track breadcrumbs: Array<Breadcrumb> = [];
|
|
144
|
+
|
|
145
|
+
constructor() {
|
|
146
|
+
super();
|
|
147
|
+
this.pageReference = this.getReferenceFromUrl();
|
|
148
|
+
// In order to prevent dispatching unnecessary highlight changes, we
|
|
149
|
+
// track these items and use their previous values for comparisons in
|
|
150
|
+
// `renderedCallback`:
|
|
151
|
+
this.state = {
|
|
152
|
+
isFetchingContent: false,
|
|
153
|
+
isFetchingDocument: false,
|
|
154
|
+
lastHighlightedSearch: "",
|
|
155
|
+
internalLinkClicked: false
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
connectedCallback(): void {
|
|
160
|
+
if (!this.pageReference?.deliverable) {
|
|
161
|
+
window.location.href = "/docs";
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
this.contentProvider = new FetchContent(
|
|
165
|
+
this.apiDomain,
|
|
166
|
+
this.allLanguages
|
|
167
|
+
);
|
|
168
|
+
this.fetchDocument().then(() => (this.loaded = true));
|
|
169
|
+
window.addEventListener("popstate", this.handlePopState);
|
|
170
|
+
|
|
171
|
+
this.searchSyncer.init();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
renderedCallback(): void {
|
|
175
|
+
this.setState({ internalLinkClicked: true });
|
|
176
|
+
const urlSectionLink =
|
|
177
|
+
this.pageReference?.hash?.split("#").length! > 1
|
|
178
|
+
? this.pageReference.hash!.split("#")[1]
|
|
179
|
+
: this.pageReference?.hash;
|
|
180
|
+
|
|
181
|
+
const contentEl = this.template.querySelector("doc-content");
|
|
182
|
+
const anchorEl =
|
|
183
|
+
urlSectionLink &&
|
|
184
|
+
(contentEl?.shadowRoot?.querySelector(`[id='${urlSectionLink}']`) ||
|
|
185
|
+
contentEl?.shadowRoot?.querySelector(
|
|
186
|
+
`[name='${urlSectionLink}']`
|
|
187
|
+
));
|
|
188
|
+
|
|
189
|
+
if (anchorEl) {
|
|
190
|
+
anchorEl.scrollIntoView();
|
|
191
|
+
|
|
192
|
+
this.setState({ internalLinkClicked: false });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (
|
|
196
|
+
(this.prevState.isFetchingContent &&
|
|
197
|
+
!this.state.isFetchingContent) ||
|
|
198
|
+
(this.prevState.isFetchingDocument &&
|
|
199
|
+
!this.state.isFetchingDocument)
|
|
200
|
+
) {
|
|
201
|
+
const prevSearchParam =
|
|
202
|
+
new URLSearchParams(this.state.lastHighlightedSearch).get(
|
|
203
|
+
"q"
|
|
204
|
+
) || "";
|
|
205
|
+
const nextSearchParam =
|
|
206
|
+
new URLSearchParams(this.pageReference.search).get("q") || "";
|
|
207
|
+
this.updateHighlighting(nextSearchParam);
|
|
208
|
+
if (prevSearchParam !== nextSearchParam) {
|
|
209
|
+
this.updateSearchInput(nextSearchParam);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
disconnectedCallback(): void {
|
|
215
|
+
window.removeEventListener("popstate", this.handlePopState);
|
|
216
|
+
this.searchSyncer.dispose();
|
|
217
|
+
if (this.listenerAttached) {
|
|
218
|
+
this.pageHeader.removeEventListener(
|
|
219
|
+
"langchange",
|
|
220
|
+
this.handleLanguageChange
|
|
221
|
+
);
|
|
222
|
+
this.listenerAttached = false;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private get languageId(): string | undefined {
|
|
227
|
+
return this.language?.id.replace("-", "_");
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private get releaseVersionId(): string | undefined {
|
|
231
|
+
return this.version?.id;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
private get deliverable(): string | undefined {
|
|
235
|
+
return this.pageReference.deliverable;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private get useOldSidebar(): boolean {
|
|
239
|
+
// Coveo is enabled and the version is greater than 51 (within the latest 3 versions)
|
|
240
|
+
// TODO: we need a better fix for version number check
|
|
241
|
+
return !(
|
|
242
|
+
this.enableCoveo &&
|
|
243
|
+
this.coveoOrganizationId &&
|
|
244
|
+
this.coveoPublicAccessToken &&
|
|
245
|
+
(!this.version?.releaseVersion ||
|
|
246
|
+
(this.version?.releaseVersion &&
|
|
247
|
+
parseInt(
|
|
248
|
+
this.version.releaseVersion.replace("v", ""),
|
|
249
|
+
10
|
|
250
|
+
) >= 53))
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
private get coveoAdvancedQueryConfig(): CoveoAdvancedQueryXMLConfig {
|
|
255
|
+
const config: {
|
|
256
|
+
locale?: string;
|
|
257
|
+
topicid?: string;
|
|
258
|
+
version?: string;
|
|
259
|
+
} = {
|
|
260
|
+
locale: this.languageId,
|
|
261
|
+
topicid: this.deliverable
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
if (this.releaseVersionId && this.releaseVersionId !== "noversion") {
|
|
265
|
+
config.version = this.releaseVersionId;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return config;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
private get pageHeader(): Header {
|
|
272
|
+
if (!this._pageHeader) {
|
|
273
|
+
this._pageHeader = document.querySelector("doc-header")!;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return this._pageHeader;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
private get sidebarValue(): string {
|
|
280
|
+
if (this.pageReference?.contentDocumentId) {
|
|
281
|
+
const hashedUri = `${
|
|
282
|
+
this.pageReference.contentDocumentId
|
|
283
|
+
}${this.normalizeHash(this.pageReference.hash)}`;
|
|
284
|
+
if (hashedUri in this.tocMap) {
|
|
285
|
+
return hashedUri;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (this.pageReference.contentDocumentId in this.tocMap) {
|
|
289
|
+
return this.pageReference.contentDocumentId;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return "";
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
private get disableVersion(): boolean {
|
|
297
|
+
return !this.availableVersions || this.availableVersions.length <= 1;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private get versionOptions(): Array<DocVersion> {
|
|
301
|
+
return this.disableVersion
|
|
302
|
+
? this.availableVersions
|
|
303
|
+
: this.availableVersions.map((version) => ({
|
|
304
|
+
...version,
|
|
305
|
+
link: {
|
|
306
|
+
href: this.pageReferenceToString({
|
|
307
|
+
...this.pageReference,
|
|
308
|
+
docId: version.url
|
|
309
|
+
})
|
|
310
|
+
}
|
|
311
|
+
}));
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private get breadcrumbPixelPerCharacter() {
|
|
315
|
+
return (
|
|
316
|
+
PIXEL_PER_CHARACTER_MAP[this.language!.id] ||
|
|
317
|
+
PIXEL_PER_CHARACTER_MAP.default
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
private get ANALYTICS_PAYLOAD() {
|
|
322
|
+
return {
|
|
323
|
+
element_title: "version picker",
|
|
324
|
+
content_category: "cta"
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
private handlePopState = (event: PopStateEvent): void =>
|
|
329
|
+
this.updatePageReference(this.getReferenceFromUrl(), event);
|
|
330
|
+
|
|
331
|
+
handleDismissVersionBanner() {
|
|
332
|
+
this.showVersionBanner = false;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
handleSelect(event: CustomEvent<{ name: string }>): void {
|
|
336
|
+
event.stopPropagation();
|
|
337
|
+
const { name } = event.detail;
|
|
338
|
+
|
|
339
|
+
if (this.sidebarValue === name) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (name) {
|
|
344
|
+
const hashIndex = name.indexOf("#");
|
|
345
|
+
const hash = hashIndex > -1 ? name.slice(hashIndex) : "";
|
|
346
|
+
|
|
347
|
+
const contentDocumentId =
|
|
348
|
+
hashIndex > -1 ? name.slice(0, hashIndex) : name;
|
|
349
|
+
this.updatePageReference({
|
|
350
|
+
...this.pageReference,
|
|
351
|
+
contentDocumentId,
|
|
352
|
+
hash
|
|
353
|
+
});
|
|
354
|
+
this.updateUrl();
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
handleNavClick(event: CustomEvent<{ pageReference: PageReference }>): void {
|
|
359
|
+
event.stopPropagation();
|
|
360
|
+
const { pageReference } = event.detail;
|
|
361
|
+
this.updatePageReference(pageReference);
|
|
362
|
+
this.updateUrl();
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
handleLanguageChange = (event: any) => {
|
|
366
|
+
if (this.language && this.language.id === event.detail) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
this.language = this.availableLanguages.find(
|
|
371
|
+
({ id }) => id === event.detail
|
|
372
|
+
);
|
|
373
|
+
this.pageReference.docId = this.language!.url;
|
|
374
|
+
|
|
375
|
+
trackGTM(event.target!, "custEv_ctaLinkClick", {
|
|
376
|
+
click_text: event.detail,
|
|
377
|
+
element_title: "language selector",
|
|
378
|
+
click_url: `${window.location.origin}${this.pageReferenceToString(
|
|
379
|
+
this.pageReference
|
|
380
|
+
)}`,
|
|
381
|
+
element_type: "link",
|
|
382
|
+
content_category: "cta"
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
this.updateUrl();
|
|
386
|
+
this.fetchDocument();
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
updatePageReference(
|
|
390
|
+
newPageReference: PageReference,
|
|
391
|
+
event: PopStateEvent | undefined = undefined
|
|
392
|
+
): void {
|
|
393
|
+
this.pageReference.hash = newPageReference.hash;
|
|
394
|
+
this.pageReference.search = newPageReference.search;
|
|
395
|
+
|
|
396
|
+
if (this.isSamePage(newPageReference)) {
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const isSameDocId = this.pageReference.docId === newPageReference.docId;
|
|
401
|
+
this.pageReference = newPageReference;
|
|
402
|
+
|
|
403
|
+
if (!isSameDocId) {
|
|
404
|
+
this.fetchDocument();
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
this.fetchContent()
|
|
409
|
+
.then(() => {
|
|
410
|
+
this.buildBreadcrumbs();
|
|
411
|
+
document.body.scrollTop = event?.state?.scroll?.value || 0;
|
|
412
|
+
})
|
|
413
|
+
.catch(handleContentError);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
abstract getReferenceFromUrl(): PageReference;
|
|
417
|
+
// getReferenceFromUrl(): PageReference {
|
|
418
|
+
// const [
|
|
419
|
+
// page,
|
|
420
|
+
// docId,
|
|
421
|
+
// deliverable,
|
|
422
|
+
// contentDocumentId
|
|
423
|
+
// ] = window.location.pathname.substr(1).split("/");
|
|
424
|
+
|
|
425
|
+
// const { origin: domain, hash, search } = window.location;
|
|
426
|
+
|
|
427
|
+
// return {
|
|
428
|
+
// contentDocumentId,
|
|
429
|
+
// deliverable,
|
|
430
|
+
// docId,
|
|
431
|
+
// domain,
|
|
432
|
+
// hash,
|
|
433
|
+
// page,
|
|
434
|
+
// search
|
|
435
|
+
// };
|
|
436
|
+
// }
|
|
437
|
+
|
|
438
|
+
isSamePage(reference: PageReference): boolean {
|
|
439
|
+
return (
|
|
440
|
+
this.pageReference.contentDocumentId ===
|
|
441
|
+
reference.contentDocumentId &&
|
|
442
|
+
this.pageReference.docId === reference.docId &&
|
|
443
|
+
this.pageReference.page === reference.page &&
|
|
444
|
+
this.pageReference.deliverable === reference.deliverable
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
abstract fetchDocument(): Promise<void>;
|
|
449
|
+
abstract fetchContent(): Promise<void>;
|
|
450
|
+
// async fetchDocument(): Promise<void> {
|
|
451
|
+
// this.setState({
|
|
452
|
+
// isFetchingDocument: true
|
|
453
|
+
// });
|
|
454
|
+
// const data = await this.contentProvider!.fetchDocumentData(
|
|
455
|
+
// this.pageReference.docId!
|
|
456
|
+
// );
|
|
457
|
+
|
|
458
|
+
// // This could be a 404 scenario.
|
|
459
|
+
// if (!data) {
|
|
460
|
+
// this.setState({
|
|
461
|
+
// isFetchingDocument: false
|
|
462
|
+
// });
|
|
463
|
+
// return;
|
|
464
|
+
// }
|
|
465
|
+
|
|
466
|
+
// this.docTitle = data.docTitle;
|
|
467
|
+
// this.tocMap = data.tocMap;
|
|
468
|
+
// this.sidebarContent = data.toc;
|
|
469
|
+
// this.version = data.version;
|
|
470
|
+
// this.language = data.language;
|
|
471
|
+
// this.availableLanguages = data.availableLanguages;
|
|
472
|
+
// this.availableVersions = data.availableVersions;
|
|
473
|
+
// this.pdfUrl = data.pdfUrl;
|
|
474
|
+
|
|
475
|
+
// this.updateHeader();
|
|
476
|
+
|
|
477
|
+
// this.buildBreadcrumbs();
|
|
478
|
+
|
|
479
|
+
// if (this.pageReference.deliverable !== data.deliverable) {
|
|
480
|
+
// this.pageReference.deliverable = data.deliverable;
|
|
481
|
+
// this.updateUrl(HistoryState.REPLACE_STATE);
|
|
482
|
+
// }
|
|
483
|
+
|
|
484
|
+
// if (this.oldVersionInfo) {
|
|
485
|
+
// this.showVersionBanner = true;
|
|
486
|
+
// }
|
|
487
|
+
|
|
488
|
+
// if (
|
|
489
|
+
// this.pageReference?.contentDocumentId?.replace(/\.htm$/, "") !==
|
|
490
|
+
// data.id
|
|
491
|
+
// ) {
|
|
492
|
+
// try {
|
|
493
|
+
// await this.fetchContent();
|
|
494
|
+
// this.setState({
|
|
495
|
+
// isFetchingDocument: false
|
|
496
|
+
// });
|
|
497
|
+
// return;
|
|
498
|
+
// } catch (error) {
|
|
499
|
+
// this.pageReference.contentDocumentId = `${data.id}.htm`;
|
|
500
|
+
// this.pageReference.hash = "";
|
|
501
|
+
// this.pageReference.search = "";
|
|
502
|
+
// this.updateUrl(HistoryState.REPLACE_STATE);
|
|
503
|
+
// }
|
|
504
|
+
// }
|
|
505
|
+
|
|
506
|
+
// this.docContent = data.content;
|
|
507
|
+
// this.addMetatags();
|
|
508
|
+
// this.setState({
|
|
509
|
+
// isFetchingDocument: false
|
|
510
|
+
// });
|
|
511
|
+
// }
|
|
512
|
+
|
|
513
|
+
// async fetchContent(): Promise<void> {
|
|
514
|
+
// this.setState({
|
|
515
|
+
// isFetchingContent: true
|
|
516
|
+
// });
|
|
517
|
+
// const data = await this.contentProvider!.fetchContent(
|
|
518
|
+
// this.pageReference.deliverable!,
|
|
519
|
+
// this.pageReference.contentDocumentId!,
|
|
520
|
+
// {
|
|
521
|
+
// language: this.language!.id,
|
|
522
|
+
// version: this.version!.id
|
|
523
|
+
// }
|
|
524
|
+
// );
|
|
525
|
+
|
|
526
|
+
// if (data) {
|
|
527
|
+
// this.docContent = data.content;
|
|
528
|
+
// this.addMetatags();
|
|
529
|
+
|
|
530
|
+
// if (!this.pageReference.hash) {
|
|
531
|
+
// document.body.scrollIntoView();
|
|
532
|
+
// }
|
|
533
|
+
// }
|
|
534
|
+
// this.setState({
|
|
535
|
+
// isFetchingContent: false
|
|
536
|
+
// });
|
|
537
|
+
// }
|
|
538
|
+
|
|
539
|
+
updateHeader(): void {
|
|
540
|
+
if (!this.pageHeader) {
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
if (this.docTitle) {
|
|
545
|
+
this.pageHeader.subtitle = this.docTitle;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
if (this.pdfUrl) {
|
|
549
|
+
this.pageHeader.bailHref = this.pdfUrl;
|
|
550
|
+
this.pageHeader.bailLabel = "PDF";
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (!this.listenerAttached) {
|
|
554
|
+
this.pageHeader.addEventListener(
|
|
555
|
+
"langchange",
|
|
556
|
+
this.handleLanguageChange
|
|
557
|
+
);
|
|
558
|
+
this.listenerAttached = true;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
this.pageHeader.languages = this.availableLanguages;
|
|
562
|
+
this.pageHeader.language = this.language?.id;
|
|
563
|
+
|
|
564
|
+
if (this.pageReference) {
|
|
565
|
+
const { docId, deliverable, page } = this.pageReference;
|
|
566
|
+
this.pageHeader.headerHref = `/${page}/${docId}/${deliverable}/`;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
updateUrl(method = HistoryState.PUSH_STATE): void {
|
|
571
|
+
//TODO: Revert this commenting - commented to work on storybook - window object is not working there.
|
|
572
|
+
|
|
573
|
+
//logCoveoPageView(this.coveoOrganizationId, this.coveoAnalyticsToken);
|
|
574
|
+
// window.history[method](
|
|
575
|
+
// {},
|
|
576
|
+
// "docs",
|
|
577
|
+
// this.pageReferenceToString(this.pageReference)
|
|
578
|
+
// );
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
private updateHighlighting(searchParam: string): void {
|
|
582
|
+
this.dispatchHighlightChange(searchParam);
|
|
583
|
+
this.setState({
|
|
584
|
+
lastHighlightedSearch: this.pageReference.search
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
private updateSearchInput(searchParam: string): void {
|
|
589
|
+
(this.template.querySelector(
|
|
590
|
+
"doc-content-layout"
|
|
591
|
+
) as any)?.setSidebarInputValue(searchParam);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
private pageReferenceToString(reference: PageReference): string {
|
|
595
|
+
const {
|
|
596
|
+
page,
|
|
597
|
+
docId,
|
|
598
|
+
deliverable,
|
|
599
|
+
contentDocumentId,
|
|
600
|
+
hash,
|
|
601
|
+
search
|
|
602
|
+
} = reference;
|
|
603
|
+
return `/${page}/${docId}/${deliverable}/${contentDocumentId}${this.normalizeSearch(
|
|
604
|
+
search!
|
|
605
|
+
)}${this.normalizeHash(hash)}`;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
private normalizeUrlPart(
|
|
609
|
+
part: string | undefined,
|
|
610
|
+
sentinel: string
|
|
611
|
+
): string {
|
|
612
|
+
return (
|
|
613
|
+
(part &&
|
|
614
|
+
(part.startsWith(sentinel!) ? part : `${sentinel}${part}`)) ||
|
|
615
|
+
""
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
private normalizeSearch(search: string): string {
|
|
620
|
+
return this.normalizeUrlPart(search, "?");
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
private normalizeHash(hash?: string): string {
|
|
624
|
+
return this.normalizeUrlPart(hash, "#");
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
private getComposedTitle(
|
|
628
|
+
topicTitle: string | null | undefined,
|
|
629
|
+
docTitle: string | undefined
|
|
630
|
+
): string {
|
|
631
|
+
// map to avoid duplicates
|
|
632
|
+
const titleMap: { [key: string]: any } = {};
|
|
633
|
+
if (topicTitle) {
|
|
634
|
+
// sometimes the h1 tag text (which is docSubTitle) contains text with new line character. For e.g, "Bulk API 2.0 Older\n Documentation",
|
|
635
|
+
// here it contains \n in the text context which needs to be removed
|
|
636
|
+
// also, there are multiple spaces in between the text, which needs to be replaced with a single space
|
|
637
|
+
const docTopicTitle = topicTitle
|
|
638
|
+
.replace(/[\t\r\n]/g, " ")
|
|
639
|
+
.replace(/ +/g, " ")
|
|
640
|
+
.trim();
|
|
641
|
+
titleMap[docTopicTitle] = true;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
if (docTitle) {
|
|
645
|
+
titleMap[docTitle] = true;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
titleMap["Salesforce Developers"] = true;
|
|
649
|
+
|
|
650
|
+
return Object.keys(titleMap).join(" | ");
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
private dispatchHighlightChange(term: string): void {
|
|
654
|
+
this.dispatchEvent(
|
|
655
|
+
new CustomEvent("highlightedtermchange", {
|
|
656
|
+
detail: term,
|
|
657
|
+
bubbles: true,
|
|
658
|
+
composed: true
|
|
659
|
+
})
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
get showBreadcrumbs(): boolean {
|
|
664
|
+
return this.breadcrumbs && this.breadcrumbs.length > 1;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
buildBreadcrumbs(): void {
|
|
668
|
+
const { contentDocumentId } = this.pageReference;
|
|
669
|
+
if (!contentDocumentId) {
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
const currentNode = this.tocMap[contentDocumentId];
|
|
674
|
+
|
|
675
|
+
if (currentNode?.parent) {
|
|
676
|
+
this.breadcrumbs = this.nodeToBreadcrumb(currentNode);
|
|
677
|
+
} else {
|
|
678
|
+
this.breadcrumbs = [];
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
private nodeToBreadcrumb(node: TreeNode): Breadcrumb[] {
|
|
683
|
+
const item = {
|
|
684
|
+
href: this.pageReferenceToString({
|
|
685
|
+
...this.pageReference,
|
|
686
|
+
contentDocumentId: node.name
|
|
687
|
+
}),
|
|
688
|
+
label: node.label
|
|
689
|
+
};
|
|
690
|
+
|
|
691
|
+
if (node.parent) {
|
|
692
|
+
return [...this.nodeToBreadcrumb(node.parent), item];
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
return [item];
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// This method take docId and drops the version from the docId.
|
|
699
|
+
// Example:
|
|
700
|
+
// Takes input string: docId = "atlas.en-us.238.0.b2b_b2c_comm_dev.meta"
|
|
701
|
+
// Output string: filteredDocId = "atlas.en-us.b2b_b2c_comm_dev.meta"
|
|
702
|
+
dropVersionFromDocId(docId: string): string {
|
|
703
|
+
if (!this.version?.id) {
|
|
704
|
+
return docId;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
const curVersion = this.version.id + ".";
|
|
708
|
+
const filteredDocId = docId.replace(curVersion, "");
|
|
709
|
+
return filteredDocId;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
addMetatags(): void {
|
|
713
|
+
const div = document.createElement("div");
|
|
714
|
+
div.innerHTML = this.docContent;
|
|
715
|
+
const docDescription = div.querySelector(".shortdesc")?.textContent;
|
|
716
|
+
const topicTitle = div.querySelector("h1")?.textContent;
|
|
717
|
+
|
|
718
|
+
const title = document.querySelector("title");
|
|
719
|
+
const composedTitle = this.getComposedTitle(topicTitle, this.docTitle);
|
|
720
|
+
|
|
721
|
+
if (title) {
|
|
722
|
+
title.textContent = composedTitle;
|
|
723
|
+
}
|
|
724
|
+
const metatitle = document.querySelector('meta[name="title"]');
|
|
725
|
+
if (metatitle) {
|
|
726
|
+
metatitle.setAttribute("content", composedTitle);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
if (docDescription) {
|
|
730
|
+
const metadescription = document.querySelector(
|
|
731
|
+
'meta[name="description"]'
|
|
732
|
+
);
|
|
733
|
+
if (metadescription) {
|
|
734
|
+
metadescription.setAttribute("content", docDescription);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
if (this.pageReference) {
|
|
739
|
+
const metadescription = document.querySelector(
|
|
740
|
+
'link[rel="canonical"]'
|
|
741
|
+
);
|
|
742
|
+
if (metadescription) {
|
|
743
|
+
const copyPageReference = { ...this.pageReference };
|
|
744
|
+
copyPageReference.docId = copyPageReference.docId
|
|
745
|
+
? this.dropVersionFromDocId(copyPageReference.docId)
|
|
746
|
+
: copyPageReference.docId;
|
|
747
|
+
metadescription.setAttribute(
|
|
748
|
+
"href",
|
|
749
|
+
window.location.protocol +
|
|
750
|
+
"//" +
|
|
751
|
+
window.location.host +
|
|
752
|
+
this.pageReferenceToString(copyPageReference)
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
this.addNoIndexMetaForOlderDocVersions();
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/**
|
|
761
|
+
* Method adds noindex, follow meta tag to the older Couch DB doc pages.
|
|
762
|
+
* Fixes W-12547462.
|
|
763
|
+
*/
|
|
764
|
+
private addNoIndexMetaForOlderDocVersions() {
|
|
765
|
+
// eslint-disable-next-line @lwc/lwc/no-document-query
|
|
766
|
+
const headTag = document.getElementsByTagName("head");
|
|
767
|
+
// this checks if the selected version is not the latest version,
|
|
768
|
+
// then it adds the noindex, follow meta tag to the older version pages.
|
|
769
|
+
const versionId = this.version!.id;
|
|
770
|
+
const docId = this.pageReference.docId;
|
|
771
|
+
|
|
772
|
+
// SEO fix:
|
|
773
|
+
// Doc id without version id is always considered latest and should be used for SEO.
|
|
774
|
+
// Condition is to find a docId which includes version id,
|
|
775
|
+
// these docs are always considered as old and should not be indexed including the preview docs.
|
|
776
|
+
if (
|
|
777
|
+
headTag.length &&
|
|
778
|
+
docId?.includes(versionId) &&
|
|
779
|
+
!document.querySelector('meta[name="robots"]')
|
|
780
|
+
) {
|
|
781
|
+
const robotsMeta = document.createElement("meta");
|
|
782
|
+
robotsMeta.setAttribute("name", "robots");
|
|
783
|
+
robotsMeta.setAttribute("content", "noindex, follow");
|
|
784
|
+
headTag[0].appendChild(robotsMeta);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|