@salesforcedevs/docs-components 0.56.2 → 0.57.1-flex-ref1

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.
@@ -3,7 +3,7 @@ import { noCase } from "no-case";
3
3
  import { sentenceCase } from "sentence-case";
4
4
  import qs from "query-string";
5
5
  import { AmfModelParser } from "./utils";
6
- import { RouteMeta } from "./route-meta";
6
+ import { normalizeBoolean } from "dxUtils/normalizers";
7
7
  import type {
8
8
  AmfConfig,
9
9
  AmfMetadataTopic,
@@ -14,94 +14,46 @@ import type {
14
14
  TopicModel,
15
15
  ReferenceVersion,
16
16
  ReferenceSetConfig,
17
- AmfMetaTopicType
17
+ AmfMetaTopicType,
18
+ RouteMeta,
19
+ ParsedMarkdownTopic
18
20
  } from "./types";
19
21
 
20
- const NAVIGATION_ITEMS = [
21
- {
22
- label: "Summary",
23
- name: "summary",
24
- childrenPropertyName: undefined,
25
- type: "summary"
26
- },
27
- {
28
- label: "Endpoints",
29
- name: "endpoints",
30
- childrenPropertyName: "endpoints",
31
- type: "endpoint"
32
- },
33
- {
34
- label: "Documentation",
35
- name: "documentation",
36
- childrenPropertyName: "docs",
37
- type: "documentation"
38
- },
39
- {
40
- label: "Types",
41
- name: "types",
42
- childrenPropertyName: "types",
43
- type: "type"
44
- },
45
- {
46
- label: "Security",
47
- name: "security",
48
- childrenPropertyName: "security",
49
- type: "security"
50
- }
51
- ];
52
-
53
- const URL_CONFIG = {
54
- summary: {
55
- urlIdentifer: "label"
56
- },
57
- endpoint: {
58
- urlIdentifer: "path"
59
- },
60
- method: {
61
- urlIdentifer: "label"
62
- },
63
- documentation: {
64
- urlIdentifer: "label"
65
- },
66
- type: {
67
- urlIdentifer: "label",
68
- prefix: "type:"
69
- },
70
- security: {
71
- urlIdentifer: "label",
72
- prefix: "security:"
73
- }
74
- };
75
-
76
- const urlHashToMetaRedirectMap = {
77
- "commerce-api-assignments:Summary": "assignments:Summary",
78
- "commerce-api-campaigns:Summary": "campaigns:Summary",
79
- "commerce-api-catalogs:Summary": "catalogs:Summary",
80
- "cdn-zones:Summary": "cdn-api-process-apis:Summary",
81
- "inventory-impex:Summary": "impex:Summary",
82
- "inventory-reservations:Summary": "inventory-reservation-service:Summary",
83
- "shopper-login-and-api-access-service:Summary": "shopper-login:Summary",
84
- "shopper-login-and-api-access-service-admin:Summary": "slas-admin:Summary",
85
- "einstein-recommendations:Summary": "einstein-api-quick-start-guide:Summary"
86
- };
22
+ import {
23
+ NAVIGATION_ITEMS,
24
+ URL_CONFIG,
25
+ REFERENCE_TYPES,
26
+ oldReferenceIdNewReferenceIdMap
27
+ } from "./constants";
87
28
 
88
29
  export default class AmfReference extends LightningElement {
89
- @api breadcrumbs?: string = null;
90
- @api sidebarHeader: string;
30
+ @api breadcrumbs?: string | null | undefined = null;
31
+ @api sidebarHeader!: string;
91
32
  @api coveoOrganizationId!: string;
92
33
  @api coveoPublicAccessToken!: string;
93
34
  @api coveoAdvancedQueryConfig!: string;
94
35
  @api coveoSearchHub!: string;
95
36
  @api useOldSidebar?: boolean = false;
37
+ @api tocTitle?: string;
38
+ @api tocOptions?: string;
39
+ @track navigation = [];
40
+ @track versions: Array<ReferenceVersion> = [];
96
41
 
97
42
  // Update this to update what component gets rendered in the content block
98
43
  @track
99
- protected topicModel: TopicModel;
44
+ protected topicModel!: TopicModel;
100
45
 
101
46
  get isVersionEnabled(): boolean {
102
47
  return !!this._referenceSetConfig?.versions?.length;
103
48
  }
104
49
 
50
+ /**
51
+ * Gives if the currently selected reference is spec based or not
52
+ */
53
+ get showSpecBasedReference(): boolean {
54
+ return this.isSpecBasedReference(this._currentReferenceId);
55
+ }
56
+
105
57
  @api
106
58
  get referenceSetConfig(): ReferenceSetConfig {
107
59
  return this._referenceSetConfig;
@@ -122,22 +74,42 @@ export default class AmfReference extends LightningElement {
122
74
  this._referenceSetConfig = refConfig;
123
75
  } catch (e) {
124
76
  this._referenceSetConfig = {
125
- refList: []
77
+ refList: [],
78
+ versions: []
126
79
  };
127
80
  }
128
81
 
82
+ this._amfConfigList = this._referenceSetConfig.refList || [];
83
+
84
+ this._amfConfigList.forEach((amfConfig) => {
85
+ this._amfConfigMap.set(amfConfig.id, amfConfig);
86
+ });
87
+
88
+ if (this._amfConfigList.length > 0) {
89
+ this._currentReferenceId =
90
+ this._referenceSetConfig.refId || this._amfConfigList[0].id;
91
+ }
92
+
129
93
  if (this.isVersionEnabled) {
130
94
  const selectedVersion = this.getSelectedVersion();
131
- this.versionToRefMap = this._referenceSetConfig.versionToRefMap;
132
- // if version is not available, then show empty - this will be the default behaviour
133
- this._amfConfig = this.versionToRefMap[selectedVersion.id] || [];
134
- this.versions = this._referenceSetConfig.versions;
95
+
96
+ /**
97
+ * If current selected is markdown based reference,
98
+ * We will assign versions once the selected item url is updated
99
+ */
100
+ if (this.isSpecBasedReference(this._currentReferenceId)) {
101
+ this.versions = this.getVersions();
102
+ }
135
103
  this.selectedVersion = selectedVersion;
136
- } else {
137
- this._amfConfig = this._referenceSetConfig.refList;
138
104
  }
139
105
 
140
- this.updateAmfConfigInView();
106
+ // This is to check if the url is hash based and redirect if needed
107
+ const redirectUrl = this.getHashBasedRedirectUrl();
108
+ if (redirectUrl) {
109
+ window.location.href = redirectUrl;
110
+ } else {
111
+ this.updateAmfConfigInView();
112
+ }
141
113
  }
142
114
 
143
115
  @api
@@ -152,53 +124,51 @@ export default class AmfReference extends LightningElement {
152
124
  }
153
125
  }
154
126
 
127
+ @api
128
+ get expandChildren() {
129
+ return this._expandChildren;
130
+ }
131
+
132
+ set expandChildren(value) {
133
+ this._expandChildren = normalizeBoolean(value);
134
+ }
135
+
155
136
  // model
156
- protected _amfConfig: AmfConfig[] = [];
157
- protected _referenceSetConfig: ReferenceSetConfig;
137
+ protected _amfConfigList: AmfConfig[] = [];
138
+ protected _amfConfigMap: Map<string, AmfConfig> = new Map();
139
+ protected _referenceSetConfig!: ReferenceSetConfig;
158
140
  protected _currentReferenceId = "";
159
141
 
142
+ protected parentReferenceUrls = [];
160
143
  protected amfMap: Record<string, AmfModelRecord> = {};
161
144
  protected amfFetchPromiseMap = {};
162
145
  protected metadata: { [key: string]: AmfMetadataTopic } = {};
163
- protected selectedTopic: AmfMetaTopicType = undefined;
164
- protected navigation = [];
146
+ protected selectedTopic?: AmfMetaTopicType = undefined;
165
147
  protected selectedSidebarValue = undefined;
166
- protected versions: Array<ReferenceVersion> = [];
167
- protected selectedVersion: ReferenceVersion = null;
168
148
 
169
- private hasRendered = false;
170
- private navAmfOrder = [];
149
+ protected selectedVersion: ReferenceVersion | null = null;
171
150
 
172
- private versionToRefMap: Map<string, Array<AmfConfig>>;
151
+ private hasRendered = false;
173
152
 
174
153
  private isParentLevelDocPhaseEnabled = false;
175
- private selectedReferenceDocPhase?: string = null;
154
+ private selectedReferenceDocPhase?: string | null = null;
155
+ private _expandChildren?: boolean = false;
176
156
 
177
157
  /**
178
- * Key for storing the currently selected reference meta query param. This will be used to save the
179
- * previously selected reference meta and restoring it when changing between reference versions.
158
+ * Key for storing the currently selected reference url. This will be used to save the
159
+ * previously selected reference url and restoring it when changing between reference versions.
180
160
  */
181
- private readonly docsReferenceMetaSessionKey: string = "docsReferenceMeta";
161
+ private readonly docsReferenceUrlSessionKey: string = "docsReferenceUrl";
182
162
 
183
163
  _boundOnApiNavigationChanged;
184
164
  _boundUpdateSelectedItemFromUrlQuery;
185
165
 
186
- get amfConfigMapped(): AmfConfig[] {
187
- return this._amfConfig.map((config) => {
188
- return config.id in this.amfMap
189
- ? Object.assign(config, this.amfMap[config.id])
190
- : config;
191
- });
192
- }
193
-
194
166
  constructor() {
195
167
  super();
196
- this._boundOnApiNavigationChanged = this.onApiNavigationChanged.bind(
197
- this
198
- );
199
- this._boundUpdateSelectedItemFromUrlQuery = this.updateSelectedItemFromUrlQuery.bind(
200
- this
201
- );
168
+ this._boundOnApiNavigationChanged =
169
+ this.onApiNavigationChanged.bind(this);
170
+ this._boundUpdateSelectedItemFromUrlQuery =
171
+ this.updateSelectedItemFromUrlQuery.bind(this);
202
172
  }
203
173
 
204
174
  connectedCallback(): void {
@@ -226,7 +196,7 @@ export default class AmfReference extends LightningElement {
226
196
  renderedCallback(): void {
227
197
  if (!this.hasRendered) {
228
198
  this.hasRendered = true;
229
- if (this._amfConfig && this._amfConfig.length) {
199
+ if (this._amfConfigList && this._amfConfigList.length) {
230
200
  // If amfConfig has a value and length, it is assumed that fetch
231
201
  // has already been called and promises stored.
232
202
  this.updateView();
@@ -234,6 +204,109 @@ export default class AmfReference extends LightningElement {
234
204
  }
235
205
  }
236
206
 
207
+ /**
208
+ * Check if the URL hash to see whether this is one we want to redirect
209
+ * See GUS W-10718771 for references where we want hash-based redirects
210
+ * Return if we needs to redirect url to updated url
211
+ */
212
+ private getHashBasedRedirectUrl(): string | undefined {
213
+ const { hash } = window.location;
214
+ let hashBasedRedirectUrl = "";
215
+ if (hash) {
216
+ const strippedHash = hash.startsWith("#") ? hash.slice(1) : hash;
217
+ const strippedHashItems = strippedHash
218
+ ? strippedHash.split(":")
219
+ : [];
220
+ if (strippedHashItems.length) {
221
+ const referenceId = strippedHashItems[0];
222
+ const meta = strippedHashItems[1];
223
+ const encodedMeta = this.getUrlEncoded(meta);
224
+ const updatedReferenceId =
225
+ oldReferenceIdNewReferenceIdMap[referenceId];
226
+ const newReferenceId = updatedReferenceId || referenceId;
227
+ const referenceItemConfig =
228
+ this.getAmfConfigWithId(newReferenceId);
229
+ if (referenceItemConfig) {
230
+ hashBasedRedirectUrl = `${referenceItemConfig.href}?meta=${encodedMeta}`;
231
+ }
232
+ }
233
+ }
234
+ return hashBasedRedirectUrl;
235
+ }
236
+
237
+ /**
238
+ * @param referenceId
239
+ * @returns AMFConfig with given reference Id
240
+ */
241
+ private getAmfConfigWithId(referenceId: string): AmfConfig | undefined {
242
+ return this._amfConfigMap.get(referenceId);
243
+ }
244
+
245
+ /**
246
+ * @param referenceId
247
+ * @returns if the reference is spec based one or not with given referenceId.
248
+ */
249
+ private isSpecBasedReference(referenceId: string): boolean {
250
+ const selectedReference = this.getAmfConfigWithId(referenceId);
251
+ return selectedReference
252
+ ? selectedReference.referenceType !== REFERENCE_TYPES.markdown
253
+ : false;
254
+ }
255
+
256
+ /*
257
+ * Refactor below method when sidebar allows sending extraData along with the name for each item.
258
+ * See if we can refactor the below method using regex.
259
+ */
260
+
261
+ /**
262
+ * @param url
263
+ * @returns reference Id from url path / selected sidebar item.
264
+ */
265
+ private getReferenceIdFromUrl(url: string): string {
266
+ let referenceId = "";
267
+ const urlItems = url.split("/references/");
268
+ if (urlItems.length > 1) {
269
+ const rightSidePart = urlItems[1];
270
+
271
+ //This covers urls like "/project-name/references/reference-id/..."
272
+ const slashSeparatorItems = rightSidePart.split("/");
273
+
274
+ //This covers urls like "/project-name/references/reference-id?meta=Summary"
275
+ const querySeparatorItems = slashSeparatorItems[0].split("?");
276
+
277
+ referenceId = querySeparatorItems[0];
278
+ }
279
+
280
+ return referenceId;
281
+ }
282
+
283
+ /**
284
+ * @returns versions to be shown in the dropdown
285
+ * For markdown based specs, Adds selected markdown topic url to same references
286
+ */
287
+ private getVersions(): Array<ReferenceVersion> {
288
+ const allVersions = this._referenceSetConfig.versions;
289
+ if (!this.isSpecBasedReference(this._currentReferenceId)) {
290
+ const currentRefMeta = this.getMarkdownReferenceMeta(
291
+ window.location.href
292
+ );
293
+ if (currentRefMeta) {
294
+ for (let i = 0; i < allVersions.length; i++) {
295
+ const versionItem = allVersions[i];
296
+ const referenceLink = versionItem.link.href;
297
+ const referenceId =
298
+ this.getReferenceIdFromUrl(referenceLink);
299
+ if (this._currentReferenceId === referenceId) {
300
+ // This is to navigate to respective topic in the changed version
301
+ versionItem.link.href = `${referenceLink}/${currentRefMeta}`;
302
+ allVersions[i] = versionItem;
303
+ }
304
+ }
305
+ }
306
+ }
307
+ return allVersions;
308
+ }
309
+
237
310
  /**
238
311
  * Returns the selected version or the first available version.
239
312
  */
@@ -247,9 +320,9 @@ export default class AmfReference extends LightningElement {
247
320
  }
248
321
 
249
322
  private updateAmfConfigInView(): void {
250
- if (this._amfConfig && this._amfConfig.length) {
323
+ if (this._amfConfigList && this._amfConfigList.length) {
251
324
  // fetch AMF Json as soon as config is set
252
- this.fetchAllAmf();
325
+ this.populateReferenceItems();
253
326
  // update() must be called after renderedCallback.
254
327
  if (this.hasRendered) {
255
328
  this.updateView();
@@ -269,17 +342,69 @@ export default class AmfReference extends LightningElement {
269
342
  }
270
343
 
271
344
  /**
272
- * Calls the fetch for each AMF in the config.
273
- * Stores each fetch promise for handling after renderCallback.
345
+ * Returns whether current location is project root path like ../example-project/references
274
346
  */
275
- private fetchAllAmf(): void {
276
- for (const [i, amfConfig] of this._amfConfig.entries()) {
277
- const p = this.fetchAmf(amfConfig).then((amfJson) => {
278
- this.updateModel(amfConfig.id, amfJson);
279
- this.assignNavigationItemsFromAmf(amfConfig.id, i);
280
- });
281
- this.amfFetchPromiseMap[amfConfig.id] = p;
347
+ private isProjectRootPath(): boolean {
348
+ return this.getReferenceIdFromUrl(window.location.href) === "";
349
+ }
350
+
351
+ /**
352
+ * Returns whether given url is parent reference path like ../example-project/references/reference-id
353
+ */
354
+ private isParentReferencePath(urlPath: string): boolean {
355
+ if (!urlPath) {
356
+ return false;
357
+ }
358
+ const parentReferenceIndex = this.parentReferenceUrls.findIndex(
359
+ (referenceUrl: string) => {
360
+ return urlPath.endsWith(referenceUrl);
361
+ }
362
+ );
363
+ return parentReferenceIndex !== -1;
364
+ }
365
+
366
+ /**
367
+ * Populates reference Items from amfConfigList and assigns it to navigation for sidebar
368
+ */
369
+ private populateReferenceItems(): void {
370
+ const navAmfOrder = [];
371
+ for (const [index, amfConfig] of this._amfConfigList.entries()) {
372
+ let navItemChildren = [];
373
+ let isChildrenLoading = false;
374
+ if (amfConfig.referenceType !== REFERENCE_TYPES.markdown) {
375
+ if (amfConfig.isSelected) {
376
+ const amfPromise = this.fetchAmf(amfConfig).then(
377
+ (amfJson) => {
378
+ this.updateModel(amfConfig.id, amfJson);
379
+ this.assignNavigationItemsFromAmf(amfConfig, index);
380
+ }
381
+ );
382
+ this.amfFetchPromiseMap[amfConfig.id] = amfPromise;
383
+ }
384
+ isChildrenLoading = true;
385
+ } else {
386
+ navItemChildren = amfConfig.topic.children;
387
+ }
388
+ // store nav items for each spec in order
389
+ navAmfOrder[index] = {
390
+ label: amfConfig.title,
391
+ name: amfConfig.href,
392
+ isExpanded:
393
+ amfConfig.isSelected ||
394
+ this.isExpandChildrenEnabled(amfConfig.id),
395
+ children: navItemChildren,
396
+ isChildrenLoading
397
+ };
398
+ this.parentReferenceUrls.push(amfConfig.href);
282
399
  }
400
+ this.navigation = navAmfOrder;
401
+ }
402
+
403
+ /**
404
+ * Returns a boolean indicating whether the children should be expanded or not.
405
+ */
406
+ private isExpandChildrenEnabled(referenceId: string): boolean {
407
+ return this.expandChildren && this._currentReferenceId === referenceId;
283
408
  }
284
409
 
285
410
  /**
@@ -311,7 +436,7 @@ export default class AmfReference extends LightningElement {
311
436
 
312
437
  /**
313
438
  * Transforms a list of model data for endpoints into corresponding
314
- * navigation list items that is compatible with dx-sidebar.
439
+ * navigation list items that are compatible with dx-sidebar.
315
440
  * Compatible with transforming AMF data parsed from both RAML and OAS spec.
316
441
  * Transforms a flat list of endpoints into a nested list based on indentation level
317
442
  * for RAML spec.
@@ -319,6 +444,7 @@ export default class AmfReference extends LightningElement {
319
444
  * @returns {array<Object>} List of navigation items
320
445
  */
321
446
  private assignEndpointNavItems(
447
+ parentReferencePath: string,
322
448
  referenceId: string,
323
449
  items: ParsedTopicModel[]
324
450
  ): NavItem[] {
@@ -326,22 +452,39 @@ export default class AmfReference extends LightningElement {
326
452
 
327
453
  items.forEach((item) => {
328
454
  item.methods?.forEach((method) => {
329
- const meta = this.addToMetadata(referenceId, "method", method);
455
+ const title =
456
+ this.getTitleForLabel(method.label) || method.method;
457
+ const meta = this.addToMetadata(
458
+ parentReferencePath,
459
+ referenceId,
460
+ "method",
461
+ method,
462
+ title
463
+ );
330
464
  methodList.push(
331
465
  Object.assign(method, {
332
- name: this.getReferencePathWithMeta(meta),
333
- label:
334
- this.getTitleForLabel(method.label) || method.method
466
+ name: this.getReferencePathWithMeta(
467
+ parentReferencePath,
468
+ meta
469
+ ),
470
+ label: title
335
471
  })
336
472
  );
337
473
  });
338
474
  });
339
-
340
475
  return methodList;
341
476
  }
342
477
 
343
- private getReferencePathWithMeta(meta: string): string {
344
- return meta ? `${window.location.pathname}?meta=${meta}` : "";
478
+ /**
479
+ * Gives URL path for reference items, Which can be used to push to the history
480
+ */
481
+ private getReferencePathWithMeta(
482
+ parentReferencePath: string,
483
+ meta: string
484
+ ): string {
485
+ // update the encoded url meta param
486
+ const encodedMeta = meta ? this.getUrlEncoded(meta) : "";
487
+ return encodedMeta ? `${parentReferencePath}?meta=${encodedMeta}` : "";
345
488
  }
346
489
 
347
490
  /**
@@ -351,12 +494,15 @@ export default class AmfReference extends LightningElement {
351
494
  * The 'endpoint' nav item may have nested children.
352
495
  */
353
496
  private assignNavigationItemsFromAmf(
354
- referenceId: string,
497
+ amfConfig: AmfConfig,
355
498
  amfIdx: number
356
499
  ): void {
500
+ const referenceId = amfConfig.id;
501
+ const parentReferencePath = amfConfig.href;
357
502
  const model = this.amfMap[referenceId].parser.parsedModel;
358
503
 
359
504
  const children = [];
505
+ const expandChildren = this.isExpandChildrenEnabled(referenceId);
360
506
 
361
507
  NAVIGATION_ITEMS.forEach(
362
508
  ({ label, name, childrenPropertyName, type }) => {
@@ -365,13 +511,18 @@ export default class AmfReference extends LightningElement {
365
511
  case "summary": {
366
512
  const summary = model[type];
367
513
  const meta = this.addToMetadata(
514
+ parentReferencePath,
368
515
  referenceId,
369
516
  type,
370
- summary
517
+ summary,
518
+ label
371
519
  );
372
520
  children.push({
373
521
  label,
374
- name: this.getReferencePathWithMeta(meta)
522
+ name: this.getReferencePathWithMeta(
523
+ parentReferencePath,
524
+ meta
525
+ )
375
526
  });
376
527
  break;
377
528
  }
@@ -385,15 +536,17 @@ export default class AmfReference extends LightningElement {
385
536
  indexedName
386
537
  );
387
538
  const childTopics = this.assignEndpointNavItems(
539
+ parentReferencePath,
388
540
  referenceId,
389
541
  model[childrenPropertyName]
390
542
  );
391
543
  children.push({
392
544
  label,
393
545
  name: this.getReferencePathWithMeta(
546
+ parentReferencePath,
394
547
  this.metadata[amfTopicId]?.meta
395
548
  ),
396
- isExpanded: false,
549
+ isExpanded: expandChildren,
397
550
  children: childTopics
398
551
  });
399
552
  }
@@ -425,19 +578,23 @@ export default class AmfReference extends LightningElement {
425
578
  children.push({
426
579
  label,
427
580
  name: this.getReferencePathWithMeta(
581
+ parentReferencePath,
428
582
  this.metadata[amfTopicId]?.meta
429
583
  ),
430
- isExpanded: false,
584
+ isExpanded: expandChildren,
431
585
  children: model[childrenPropertyName].map(
432
586
  (topic) => {
433
587
  const meta = this.addToMetadata(
588
+ parentReferencePath,
434
589
  referenceId,
435
590
  type,
436
- topic
591
+ topic,
592
+ topic.label
437
593
  );
438
594
  return {
439
595
  label: topic.label,
440
596
  name: this.getReferencePathWithMeta(
597
+ parentReferencePath,
441
598
  meta
442
599
  )
443
600
  };
@@ -449,46 +606,41 @@ export default class AmfReference extends LightningElement {
449
606
  }
450
607
  );
451
608
 
452
- // store nav items for each spec in order
453
- this.navAmfOrder[amfIdx] = {
454
- label: model.title,
455
- name: this.getReferencePathWithMeta(`${referenceId}-root`),
456
- isExpanded: amfIdx === 0, // only expand the first spec
457
- children
609
+ this.navigation[amfIdx] = {
610
+ ...this.navigation[amfIdx],
611
+ children,
612
+ isChildrenLoading: false
458
613
  };
459
-
460
- // update navigation with each specs nav items as they become available
461
- // navigation has to be an array because dx-sidebar expects an array.
462
- const navigation = [];
463
- for (const navAmf of this.navAmfOrder) {
464
- if (navAmf) {
465
- navigation.push(navAmf);
466
- }
467
- }
468
- this.navigation = navigation;
614
+ this.navigation = [...this.navigation];
469
615
  }
470
616
 
471
617
  protected addToMetadata(
618
+ parentReferencePath: string,
472
619
  referenceId: string,
473
620
  type: string,
474
- topic: { id: string; domId: string }
475
- ): string {
621
+ topic: { id: string; domId: string },
622
+ navTitle: string
623
+ ): string | undefined {
476
624
  const { urlIdentifer, prefix } = URL_CONFIG[type];
477
625
 
478
626
  // encodeURI to avoid special characters in the URL meta.
479
627
  const identifier =
480
628
  topic[urlIdentifer] && this.encodeIdentifier(topic[urlIdentifer]);
481
- const meta = prefix
482
- ? `${referenceId}:${prefix}${identifier}`
483
- : `${referenceId}:${identifier}`;
484
- this.metadata[meta] = {
485
- meta: meta,
486
- referenceId: referenceId,
487
- amfId: topic.id,
488
- elementId: topic.domId,
489
- identifier,
490
- type
491
- };
629
+ let meta;
630
+ // Assuming that there will be an identifier always
631
+ if (identifier) {
632
+ meta = prefix ? `${prefix}${identifier}` : `${identifier}`;
633
+ this.metadata[meta] = {
634
+ parentReferencePath,
635
+ meta,
636
+ referenceId,
637
+ amfId: topic.id,
638
+ elementId: topic.domId,
639
+ identifier,
640
+ type,
641
+ navTitle
642
+ };
643
+ }
492
644
  return meta;
493
645
  }
494
646
 
@@ -547,54 +699,25 @@ export default class AmfReference extends LightningElement {
547
699
  }
548
700
 
549
701
  /**
550
- * Parses url query params without decoding of params
702
+ * Parses URL query params without decoding of params
551
703
  */
552
- private parseParams(path: string): qs.ParsedQuery<string> {
553
- if (!path) {
704
+ private parseParams(params: string): qs.ParsedQuery<string> {
705
+ if (!params) {
554
706
  return {};
555
707
  }
556
- return qs.parse(path, {
708
+ return qs.parse(params, {
557
709
  decode: false
558
710
  });
559
711
  }
560
712
 
561
- /**
562
- * Gets the portion from the URL query param 'meta'.
563
- */
564
- protected getCurrentRefMeta(
565
- previousRefMetaInSession?: string
566
- ): RouteMeta | null {
567
- const path = window.location.search;
568
- const urlParams = this.parseParams(path);
569
- let meta = urlParams.meta as string;
570
- let routeMeta = null;
571
- if (previousRefMetaInSession) {
572
- const refParts = previousRefMetaInSession.split(":");
573
- const newRefId = refParts.length > 0 ? refParts[0] : null;
574
- const [, type, topicId] = previousRefMetaInSession.split(":");
575
- meta = newRefId ? [newRefId, type, topicId].join(":") : null;
576
- } else if (!meta) {
577
- // If no `meta` explicitly exists, check the URL hash to see whether this is one we
578
- // want to redirect (see GUS W-10718771 for one reference where we want hash-based
579
- // redirects)
580
- const { hash } = window.location;
581
- const strippedHash = hash.startsWith("#") ? hash.slice(1) : hash;
582
- meta = urlHashToMetaRedirectMap[strippedHash];
583
- }
584
- if (meta) {
585
- routeMeta = new RouteMeta(meta);
586
- }
587
- return routeMeta;
588
- }
589
-
590
713
  /**
591
714
  * Normalizes topic identifier by replacing spaces with '+'
592
715
  * and running encodeURI() on it
593
- * @param identifer raw identifer for a topic as parsed from the spec file
716
+ * @param identifier raw identifier for a topic as parsed from the spec file
594
717
  * @returns normalized and encoded identifier
595
718
  */
596
- protected encodeIdentifier(identifer: string): string {
597
- let result = identifer.trim();
719
+ protected encodeIdentifier(identifier: string): string {
720
+ let result = identifier.trim();
598
721
  result = result.replace(new RegExp(/\s+/, "g"), "+");
599
722
  return encodeURI(result);
600
723
  }
@@ -606,86 +729,134 @@ export default class AmfReference extends LightningElement {
606
729
  return `${referenceId}:${id}`;
607
730
  }
608
731
 
609
- protected updateSelectedItemFromUrlQuery(): void {
610
- const currentMeta: RouteMeta | null = this.getCurrentRefMeta();
611
- const metadata = currentMeta && this.getMetadataByUrlQuery(currentMeta);
612
- if (metadata) {
613
- const {
614
- referenceId,
615
- amfId,
616
- type,
617
- elementId
618
- }: AmfMetadataTopic = metadata;
619
- this.loadContent(
620
- referenceId,
621
- amfId,
622
- type,
623
- elementId,
624
- currentMeta.meta
625
- );
626
- }
627
- }
628
- protected onApiNavigationChanged(): void {
629
- // The API Navigation event will always intend to navigate within the current reference
630
- const metadata = this.metadata[
631
- this.getReferencePathWithMeta(this.selectedTopic.meta)
632
- ];
633
- const {
634
- referenceId,
635
- amfId,
636
- type,
637
- elementId
638
- }: AmfMetadataTopic = metadata;
639
- this.loadContent(referenceId, amfId, type, elementId, metadata.meta);
640
- this.updateUrlWithSelected();
641
- }
642
-
643
- protected updateUrlWithSelected(meta?: string): void {
732
+ protected updateUrlWithSelected(
733
+ parentReferencePath: string,
734
+ meta?: string
735
+ ): void {
644
736
  if (meta) {
737
+ // update the encoded url meta param
738
+ const encodedMeta = this.getUrlEncoded(meta);
645
739
  window.history.pushState(
646
740
  {},
647
741
  "",
648
- `${window.location.pathname}?meta=${meta}`
742
+ `${parentReferencePath}?meta=${encodedMeta}`
649
743
  );
650
744
  }
651
745
  }
652
746
 
653
747
  /**
654
- * Does a replace on the url meta, so it does not create a history entry.
748
+ * Does a replace on the URL meta, so it does not create a history entry.
655
749
  */
656
- protected replaceUrlWithSelected(meta?: string): void {
750
+ protected replaceUrlWithSelected(
751
+ parentReferencePath: string,
752
+ meta?: string
753
+ ): void {
657
754
  if (meta) {
755
+ // update the encoded url meta param
756
+ const encodedMeta = this.getUrlEncoded(meta);
658
757
  window.history.replaceState(
659
758
  {},
660
759
  "",
661
- `${window.location.pathname}?meta=${meta}`
760
+ `${parentReferencePath}?meta=${encodedMeta}`
662
761
  );
663
762
  }
664
763
  }
665
764
 
765
+ /**
766
+ * This method gets called when the user navigates back and forth using browser arrows
767
+ * Updates content depending on the type of reference - spec based or markdown
768
+ */
769
+ protected updateSelectedItemFromUrlQuery(): void {
770
+ const specBasedReference = this.isSpecBasedReference(
771
+ this._currentReferenceId
772
+ );
773
+ if (specBasedReference) {
774
+ const currentMeta: RouteMeta | null = this.getReferenceMetaInfo(
775
+ window.location.href
776
+ );
777
+ const metadata =
778
+ currentMeta && this.getMetadataByUrlQuery(currentMeta);
779
+ if (metadata) {
780
+ const {
781
+ parentReferencePath,
782
+ referenceId,
783
+ amfId,
784
+ type,
785
+ elementId
786
+ }: AmfMetadataTopic = metadata;
787
+ this.loadSpecReferenceContent(
788
+ parentReferencePath,
789
+ referenceId,
790
+ amfId,
791
+ type,
792
+ elementId,
793
+ currentMeta.meta
794
+ );
795
+ }
796
+ } else {
797
+ this.loadMarkdownBasedReference();
798
+ }
799
+ }
800
+
801
+ /**
802
+ * The API Navigation event will always intend to navigate within the current reference
803
+ * @param event
804
+ */
805
+ protected onApiNavigationChanged(): void {
806
+ const specBasedReference = this.isSpecBasedReference(
807
+ this._currentReferenceId
808
+ );
809
+ if (specBasedReference) {
810
+ const { meta } = this.selectedTopic;
811
+ const metadata = this.metadata[meta];
812
+ if (metadata) {
813
+ const {
814
+ parentReferencePath,
815
+ referenceId,
816
+ amfId,
817
+ type,
818
+ elementId
819
+ }: AmfMetadataTopic = metadata;
820
+ this.loadSpecReferenceContent(
821
+ parentReferencePath,
822
+ referenceId,
823
+ amfId,
824
+ type,
825
+ elementId,
826
+ metadata.meta
827
+ );
828
+ }
829
+ } else {
830
+ this.loadMarkdownBasedReference();
831
+ }
832
+ }
833
+
666
834
  /**
667
835
  * Updates the currently selected amf and topic
668
836
  */
669
- protected loadContent(
837
+ protected loadSpecReferenceContent(
838
+ parentReferencePath: string,
670
839
  referenceId: string,
671
840
  amfId: string,
672
841
  type: string,
673
- elementId = "",
674
- meta = ""
842
+ elementId: string,
843
+ meta: string
675
844
  ): void {
676
845
  this.selectedTopic = {
677
846
  referenceId,
847
+ parentReferencePath,
678
848
  amfId,
679
849
  elementId,
680
850
  type,
681
851
  meta
682
852
  };
683
- this.selectedSidebarValue = this.getReferencePathWithMeta(meta);
853
+ this.selectedSidebarValue = this.getReferencePathWithMeta(
854
+ parentReferencePath,
855
+ meta
856
+ );
684
857
 
685
858
  this.handleSelectedItem();
686
859
 
687
- // Ensures that the URL always has the meta, that way we don't get two history entries for summary
688
- this.replaceUrlWithSelected(meta);
689
860
  this.updateDocPhase();
690
861
  }
691
862
 
@@ -696,10 +867,9 @@ export default class AmfReference extends LightningElement {
696
867
  /* If parent level doc phase is enabled, Individual reference level doc phase should not be considered */
697
868
 
698
869
  if (!this.isParentLevelDocPhaseEnabled) {
699
- const referenceId = this.selectedTopic?.referenceId;
700
- const selectedReference = this._amfConfig.find(
870
+ const selectedReference = this._amfConfigList.find(
701
871
  (referenceItem: AmfConfig) => {
702
- return referenceItem.id === referenceId;
872
+ return referenceItem.id === this._currentReferenceId;
703
873
  }
704
874
  );
705
875
  if (selectedReference) {
@@ -711,88 +881,414 @@ export default class AmfReference extends LightningElement {
711
881
  }
712
882
 
713
883
  /**
714
- * Updates the DOM on first load
884
+ * Returns the decoded meta query param from given Url as it is being used internally.
715
885
  */
716
- updateView(): void {
717
- let referenceId: string;
718
- let topicId = "";
719
- const previousRefMetaInSession = window.sessionStorage.getItem(
720
- this.docsReferenceMetaSessionKey
886
+ getMetaFromUrl(referenceUrl: string): string {
887
+ const indexOfQueryParam = referenceUrl.indexOf("?");
888
+ const urlPath = referenceUrl.substring(
889
+ indexOfQueryParam >= 0 ? indexOfQueryParam : referenceUrl.length
721
890
  );
722
- window.sessionStorage.removeItem(this.docsReferenceMetaSessionKey);
723
- const currentMeta = this.getCurrentRefMeta(previousRefMetaInSession);
724
- if (currentMeta) {
725
- referenceId = currentMeta.referenceId;
726
- topicId = currentMeta.topicId;
727
- if (!this.amfFetchPromiseMap[referenceId]) {
728
- // This could happen if they specify a bad query value.
729
- // In this case, we'll do the logic below to use the default amf
730
- referenceId = null;
891
+ const meta = this.parseParams(urlPath).meta as string;
892
+ // Always get the meta query param encoded and decode it and store it for internal use
893
+ // This has 2 advantages,
894
+ // 1. Supports backward compatible meta query param, so there is no need for redirects.
895
+ // 2. Supports Prerender and Coveo for their crawling.
896
+ const encodedMeta = meta && this.getUrlEncoded(meta);
897
+ const decodedMeta = encodedMeta && decodeURIComponent(encodedMeta);
898
+ return decodedMeta || "";
899
+ }
900
+
901
+ /**
902
+ *
903
+ * @returns meta for given markdown based referenceUrl
904
+ * Consider last topic url in ../references/reference-name/example.html
905
+ */
906
+ getMarkdownReferenceMeta(referenceUrl: string): string {
907
+ let meta = "";
908
+ if (referenceUrl) {
909
+ const slashSeparatorItems = referenceUrl.split("/");
910
+ const lastItem =
911
+ slashSeparatorItems[slashSeparatorItems.length - 1];
912
+ if (lastItem.endsWith(".html")) {
913
+ meta = lastItem;
731
914
  }
732
915
  }
916
+ return meta;
917
+ }
918
+
919
+ /**
920
+ * Gets the encoded url.
921
+ * This method will return the encoded url for 2 cases,
922
+ * 1. If the url is encoded already
923
+ * 2. If the url is decoded
924
+ */
925
+ getUrlEncoded(url: string) {
926
+ // if url matches, then return the encoded url.
927
+ if (decodeURIComponent(url) === url) {
928
+ return encodeURIComponent(url);
929
+ }
930
+ // return the encoded url.
931
+ return this.getUrlEncoded(decodeURIComponent(url));
932
+ }
933
+
934
+ /**
935
+ *
936
+ * @returns RouteMeta object for given referenceUrl
937
+ * referenceId - gets referenceId from url
938
+ * For spec based references gets meta parm from url and then topicId & type from meta
939
+ * For markdown based references gets topicId as last html path in the name, meta & type will be empty
940
+ */
941
+ getReferenceMetaInfo(referenceUrl: string): RouteMeta | undefined {
942
+ let metaReferenceInfo;
943
+ if (referenceUrl) {
944
+ const referenceId = this.getReferenceIdFromUrl(referenceUrl);
945
+ let meta = "";
946
+ let topicId = "";
947
+ let type = "";
948
+ if (this.isSpecBasedReference(referenceId)) {
949
+ meta = this.getMetaFromUrl(referenceUrl);
950
+ if (meta) {
951
+ if (meta.includes(":")) {
952
+ const metaInfo = meta.split(":");
953
+ type = metaInfo[0];
954
+ topicId = metaInfo[1] || type;
955
+ } else {
956
+ topicId = meta;
957
+ }
958
+ }
959
+ } else {
960
+ topicId = this.getMarkdownReferenceMeta(referenceUrl);
961
+ }
962
+ metaReferenceInfo = {
963
+ referenceId,
964
+ meta,
965
+ topicId,
966
+ type
967
+ };
968
+ }
969
+ return metaReferenceInfo;
970
+ }
733
971
 
734
- if (!referenceId) {
735
- referenceId = this._amfConfig[0].id;
736
- if (!this.amfFetchPromiseMap[referenceId]) {
737
- // This should never happen.
738
- referenceId = Object.keys(this.amfFetchPromiseMap)[0];
972
+ /**
973
+ * Finds and returns referenceUrl and topicTitle if given topic url matches
974
+ */
975
+ getReferenceDetailsInGivenTopics(
976
+ topics: ParsedMarkdownTopic[],
977
+ topicMeta: string
978
+ ): { referenceUrl: string; topicTitle: string } {
979
+ let referenceUrl = "";
980
+ let topicTitle = "";
981
+ for (let i = 0; i < topics.length; i++) {
982
+ const topic = topics[i];
983
+ const meta = this.getMarkdownReferenceMeta(topic.link.href);
984
+ const childTopics = topic.children;
985
+ if (meta === topicMeta) {
986
+ referenceUrl = topic.link.href;
987
+ topicTitle = topic.label;
988
+ } else if (childTopics && childTopics.length) {
989
+ const referenceDetails = this.getReferenceDetailsInGivenTopics(
990
+ childTopics,
991
+ topicMeta
992
+ );
993
+ referenceUrl = referenceDetails.referenceUrl;
994
+ topicTitle = referenceDetails.topicTitle;
995
+ }
996
+ if (referenceUrl && topicTitle) {
997
+ break;
739
998
  }
740
999
  }
1000
+ return {
1001
+ referenceUrl,
1002
+ topicTitle
1003
+ };
1004
+ }
1005
+
1006
+ /**
1007
+ * Gives referenceUrl and topicTitle for given markdown topic url
1008
+ */
1009
+ getRefDetailsForGivenTopicMeta(
1010
+ referenceId: string,
1011
+ topicMeta: string
1012
+ ): { referenceUrl: string; topicTitle: string } | undefined {
1013
+ const amfConfig = this.getAmfConfigWithId(referenceId);
1014
+ let referenceDetails;
1015
+ if (amfConfig) {
1016
+ const topics = amfConfig.topic?.children || [];
1017
+ referenceDetails = this.getReferenceDetailsInGivenTopics(
1018
+ topics,
1019
+ topicMeta
1020
+ );
1021
+ }
1022
+ return referenceDetails;
1023
+ }
741
1024
 
742
- // Wait till the AMF is loaded.
743
- this.amfFetchPromiseMap[referenceId].then(() => {
744
- let topic = this.getMetadataByIdentifier(referenceId, topicId);
745
- if (!topic) {
746
- // Doesn't exist, let's use the summary.
747
- topic = this.getMetadataByType(referenceId, "summary");
1025
+ /**
1026
+ * Updates the DOM on the first load
1027
+ */
1028
+ updateView(): void {
1029
+ const previousRefUrlInSession = window.sessionStorage.getItem(
1030
+ this.docsReferenceUrlSessionKey
1031
+ );
1032
+ window.sessionStorage.removeItem(this.docsReferenceUrlSessionKey);
1033
+ let previousRefInfo = this.getReferenceMetaInfo(
1034
+ previousRefUrlInSession
1035
+ );
1036
+
1037
+ // For spec based reference, We should consider urlData to show same topic when user reloads after navigating to specific topic
1038
+ if (!previousRefInfo) {
1039
+ const currentUrl = window.location.href;
1040
+ const urlReferenceId = this.getReferenceIdFromUrl(currentUrl);
1041
+ if (urlReferenceId && this.isSpecBasedReference(urlReferenceId)) {
1042
+ if (
1043
+ !this.isProjectRootPath() &&
1044
+ !this.isParentReferencePath(currentUrl)
1045
+ ) {
1046
+ previousRefInfo = this.getReferenceMetaInfo(currentUrl);
1047
+ }
748
1048
  }
1049
+ }
1050
+
1051
+ let referenceId: string;
1052
+ let topicId = "";
1053
+
1054
+ if (
1055
+ previousRefInfo &&
1056
+ this._amfConfigMap.has(previousRefInfo.referenceId)
1057
+ ) {
1058
+ referenceId = previousRefInfo.referenceId;
1059
+ topicId = previousRefInfo.topicId;
1060
+ } else {
1061
+ referenceId = this._currentReferenceId;
1062
+ }
1063
+
1064
+ const specBasedReference = this.isSpecBasedReference(referenceId);
1065
+ if (specBasedReference) {
1066
+ // Wait till the AMF is loaded.
1067
+ this.amfFetchPromiseMap[referenceId].then(() => {
1068
+ let selectedItemMetaData = this.getMetadataByIdentifier(
1069
+ referenceId,
1070
+ topicId
1071
+ );
1072
+ if (!selectedItemMetaData) {
1073
+ // Doesn't exist, let's use the summary.
1074
+ selectedItemMetaData = this.getMetadataByType(
1075
+ referenceId,
1076
+ "summary"
1077
+ );
1078
+ }
749
1079
 
750
- if (topic) {
751
- this.loadContent(
752
- topic.referenceId,
753
- topic.amfId,
754
- topic.type,
755
- "",
756
- topic.meta
1080
+ if (selectedItemMetaData) {
1081
+ this.loadSpecReferenceContent(
1082
+ selectedItemMetaData.parentReferencePath,
1083
+ selectedItemMetaData.referenceId,
1084
+ selectedItemMetaData.amfId,
1085
+ selectedItemMetaData.type,
1086
+ "",
1087
+ selectedItemMetaData.meta
1088
+ );
1089
+ this.updateUrlWithSelected(
1090
+ selectedItemMetaData.parentReferencePath,
1091
+ selectedItemMetaData.meta
1092
+ );
1093
+ this.updateNavTitleMetaTag(selectedItemMetaData.navTitle);
1094
+ }
1095
+ });
1096
+ } else {
1097
+ let invalidTopicReferenceUrl = "";
1098
+ if (topicId) {
1099
+ const referenceDetails = this.getRefDetailsForGivenTopicMeta(
1100
+ referenceId,
1101
+ topicId
757
1102
  );
1103
+ const selectedItemUrl = referenceDetails?.referenceUrl;
1104
+ if (!selectedItemUrl) {
1105
+ invalidTopicReferenceUrl = previousRefUrlInSession;
1106
+ }
758
1107
  }
759
- });
1108
+ this.loadMarkdownBasedReference(invalidTopicReferenceUrl);
1109
+ }
760
1110
  }
761
1111
 
762
1112
  /**
763
- * Currently, used to handle the version change and storing the current meta query param to the session storage.
1113
+ * Navigates to reference of the given URL
1114
+ * @param url
764
1115
  */
765
- handleVersionChange(): void {
766
- const path = window.location.search;
767
- const urlParams = this.parseParams(path);
768
- const meta = urlParams.meta as string;
769
- if (meta) {
770
- window.sessionStorage.setItem(
771
- this.docsReferenceMetaSessionKey,
772
- meta
1116
+ private loadNewReferenceItem(url: string): void {
1117
+ const referenceId = this.getReferenceIdFromUrl(url);
1118
+ const referenceItem = this.getAmfConfigWithId(referenceId);
1119
+ if (referenceItem) {
1120
+ window.location.href = referenceItem.href;
1121
+ }
1122
+ }
1123
+
1124
+ /**
1125
+ * @param referenceUrl to which user wants to navigate
1126
+ * Redirect to first sub item if it's root level item, otherwise content will be loaded
1127
+ * Push the history as a first child item
1128
+ * set selected sidebar value as a pathname
1129
+ */
1130
+
1131
+ private loadMarkdownBasedReference(referenceUrl?: string): void {
1132
+ let referenceId = "";
1133
+ const currentUrl = window.location.href;
1134
+ if (this.isProjectRootPath()) {
1135
+ /**
1136
+ * CASE1: This case is to consider when the user navigates to references by clicking a project card
1137
+ * Ex: /docs/example-project/references should navigate to the first topic in the first reference
1138
+ */
1139
+ referenceId = this._currentReferenceId;
1140
+ } else if (this.isParentReferencePath(referenceUrl)) {
1141
+ /**
1142
+ * CASE2: This case is to navigate to respective reference when the user clicked on root item
1143
+ * Ex: .../references/markdown-ref should navigate to first topic.
1144
+ */
1145
+ referenceId = this.getReferenceIdFromUrl(referenceUrl);
1146
+ } else if (this.isParentReferencePath(currentUrl)) {
1147
+ /**
1148
+ * CASE3: This case is to navigate to respective reference when the user entered url with reference id
1149
+ * Ex: .../references/markdown-ref should navigate to first topic.
1150
+ */
1151
+ referenceId = this.getReferenceIdFromUrl(currentUrl);
1152
+ } else if (referenceUrl) {
1153
+ /**
1154
+ * CASE4: This case is to navigate to first item when we don't have topic in the selected version
1155
+ * Ex: .../references/markdown-ref/not-existed-topic-url should navigate to first topic.
1156
+ */
1157
+ const referenceMeta = this.getMarkdownReferenceMeta(referenceUrl);
1158
+ const selectedItemRefId = this.getReferenceIdFromUrl(referenceUrl);
1159
+ const referenceDetails = this.getRefDetailsForGivenTopicMeta(
1160
+ selectedItemRefId,
1161
+ referenceMeta
773
1162
  );
1163
+ const selectedItemUrl = referenceDetails?.referenceUrl;
1164
+ if (!selectedItemUrl) {
1165
+ referenceId = this.getReferenceIdFromUrl(referenceUrl);
1166
+ }
1167
+ }
1168
+
1169
+ let isRedirecting = false;
1170
+ if (referenceId) {
1171
+ const amfConfig = this.getAmfConfigWithId(referenceId);
1172
+ let redirectReferenceUrl = "";
1173
+ if (amfConfig) {
1174
+ const childrenItems = amfConfig.topic.children;
1175
+ if (childrenItems.length > 0) {
1176
+ redirectReferenceUrl = childrenItems[0].link.href;
1177
+ }
1178
+ }
1179
+ if (redirectReferenceUrl) {
1180
+ if (this.isParentReferencePath(referenceUrl)) {
1181
+ // This is for CASE2 mentioned above, Where we need to navigate user to respective href
1182
+ isRedirecting = true;
1183
+ window.location.href = redirectReferenceUrl;
1184
+ } else {
1185
+ // This is for CASE 1,3 and 4 mentioned above, Where we need to update the browser history
1186
+ window.history.replaceState({}, "", redirectReferenceUrl);
1187
+ }
1188
+ }
1189
+ }
1190
+ if (!isRedirecting) {
1191
+ const currentReferenceUrl = window.location.href;
1192
+ const referenceMeta =
1193
+ this.getMarkdownReferenceMeta(currentReferenceUrl);
1194
+ const selectedItemRefId =
1195
+ this.getReferenceIdFromUrl(currentReferenceUrl);
1196
+ const referenceDetails = this.getRefDetailsForGivenTopicMeta(
1197
+ selectedItemRefId,
1198
+ referenceMeta
1199
+ );
1200
+ if (referenceDetails) {
1201
+ this.updateNavTitleMetaTag(referenceDetails.topicTitle);
1202
+ }
1203
+
1204
+ this.versions = this.getVersions();
1205
+ this.updateDocPhase();
1206
+ this.selectedSidebarValue = window.location.pathname;
1207
+ }
1208
+ }
1209
+
1210
+ /**
1211
+ * Currently, used to handle the version change and store the current reference Url.
1212
+ */
1213
+ handleVersionChange(): void {
1214
+ const currentUrl = window.location.href;
1215
+ window.sessionStorage.setItem(
1216
+ this.docsReferenceUrlSessionKey,
1217
+ currentUrl
1218
+ );
1219
+ }
1220
+
1221
+ private updateNavTitleMetaTag(navTitle = ""): void {
1222
+ // this is required to update the nav title meta tag.
1223
+ // eslint-disable-next-line @lwc/lwc/no-document-query
1224
+ const metaNavTitle = document.querySelector('meta[name="nav-title"]');
1225
+ if (metaNavTitle && navTitle) {
1226
+ metaNavTitle.setAttribute("content", navTitle);
774
1227
  }
775
1228
  }
776
1229
 
777
1230
  onNavSelect(event: CustomEvent): void {
778
1231
  const name = event.detail.name;
779
- const indexOfQueryParam = name.indexOf("?");
780
- const urlPath = name.substring(
781
- indexOfQueryParam >= 0 ? indexOfQueryParam : name.length
782
- );
783
- const metaVal = this.parseParams(urlPath).meta as string;
784
- const currentSelectedMeta = this.selectedTopic.meta;
1232
+ if (name) {
1233
+ const urlReferenceId = this.getReferenceIdFromUrl(name);
1234
+ const specBasedReference =
1235
+ this.isSpecBasedReference(urlReferenceId);
1236
+ if (specBasedReference) {
1237
+ const metaVal = this.getMetaFromUrl(name);
1238
+ const currentSelectedMeta = this.selectedTopic
1239
+ ? this.selectedTopic.meta
1240
+ : "";
1241
+
1242
+ if (metaVal && metaVal === currentSelectedMeta) {
1243
+ // selecting the same nav item, skip update
1244
+ return;
1245
+ }
785
1246
 
786
- if (metaVal === currentSelectedMeta) {
787
- // selecting the same nav item, skip update
788
- return;
1247
+ const metadata = this.metadata[metaVal];
1248
+ if (metadata) {
1249
+ const {
1250
+ parentReferencePath,
1251
+ referenceId,
1252
+ amfId,
1253
+ type,
1254
+ elementId
1255
+ } = metadata;
1256
+ this.loadSpecReferenceContent(
1257
+ parentReferencePath,
1258
+ referenceId,
1259
+ amfId,
1260
+ type,
1261
+ elementId,
1262
+ metaVal
1263
+ );
1264
+ this.updateUrlWithSelected(parentReferencePath, metaVal);
1265
+ this.updateNavTitleMetaTag(metadata.navTitle);
1266
+ } else {
1267
+ if (this.isParentReferencePath(name)) {
1268
+ this.loadNewReferenceItem(name);
1269
+ }
1270
+ }
1271
+ } else {
1272
+ this.loadMarkdownBasedReference(name);
1273
+ }
789
1274
  }
1275
+ }
790
1276
 
791
- const metadata = this.metadata[metaVal];
792
- if (metadata) {
793
- const { referenceId, amfId, type, elementId } = metadata;
794
- this.loadContent(referenceId, amfId, type, elementId, metaVal);
795
- this.updateUrlWithSelected(metaVal);
1277
+ onExpandCollapse(event: CustomEvent): void {
1278
+ const { name, isSelectAction, isExpanded } = event.detail;
1279
+ if (!isSelectAction && isExpanded) {
1280
+ const referenceId = this.getReferenceIdFromUrl(name);
1281
+ const currentUrl = window.location.href;
1282
+ const currentReferenceId = this.getReferenceIdFromUrl(currentUrl);
1283
+ //No need to do anything if user is expanding currently selected reference
1284
+ if (referenceId !== currentReferenceId) {
1285
+ const isSpecBasedReference =
1286
+ this.isSpecBasedReference(referenceId);
1287
+ if (isSpecBasedReference) {
1288
+ // Perform functionality same as item selection
1289
+ this.onNavSelect(event);
1290
+ }
1291
+ }
796
1292
  }
797
1293
  }
798
1294