@salesforcedevs/docs-components 1.28.5-redoc-alpha → 1.28.5-redoc-alpha2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforcedevs/docs-components",
3
- "version": "1.28.5-redoc-alpha",
3
+ "version": "1.28.5-redoc-alpha2",
4
4
  "description": "Docs Lightning web components for DSC",
5
5
  "license": "MIT",
6
6
  "main": "index.js",
@@ -38,6 +38,7 @@ type NavigationItem = {
38
38
  isExpanded: boolean;
39
39
  children: ParsedMarkdownTopic[];
40
40
  isChildrenLoading: boolean;
41
+ renderWith?: string;
41
42
  };
42
43
 
43
44
  export default class AmfReference extends LightningElement {
@@ -147,6 +148,19 @@ export default class AmfReference extends LightningElement {
147
148
 
148
149
  this._amfConfigList = this._referenceSetConfig.refList || [];
149
150
 
151
+ // If the framework didn't tag the reference, infer redoc rendering
152
+ // from the config shape: spec-based references with no AMF URL are
153
+ // shipped as raw spec sources for Redoc to render directly.
154
+ this._amfConfigList.forEach((amfConfig) => {
155
+ if (
156
+ !amfConfig.renderWith &&
157
+ amfConfig.referenceType !== REFERENCE_TYPES.markdown &&
158
+ !amfConfig.amf
159
+ ) {
160
+ amfConfig.renderWith = RENDER_WITH.redoc;
161
+ }
162
+ });
163
+
150
164
  this._amfConfigList.forEach((amfConfig) => {
151
165
  this._amfConfigMap.set(amfConfig.id, amfConfig);
152
166
  });
@@ -252,6 +266,7 @@ export default class AmfReference extends LightningElement {
252
266
 
253
267
  _boundOnApiNavigationChanged;
254
268
  _boundUpdateSelectedItemFromUrlQuery;
269
+ _boundOnPageShow;
255
270
 
256
271
  constructor() {
257
272
  super();
@@ -260,6 +275,7 @@ export default class AmfReference extends LightningElement {
260
275
  this.onApiNavigationChanged.bind(this);
261
276
  this._boundUpdateSelectedItemFromUrlQuery =
262
277
  this.updateSelectedItemFromUrlQuery.bind(this);
278
+ this._boundOnPageShow = this.onPageShow.bind(this);
263
279
  }
264
280
 
265
281
  connectedCallback(): void {
@@ -271,6 +287,7 @@ export default class AmfReference extends LightningElement {
271
287
  "popstate",
272
288
  this._boundUpdateSelectedItemFromUrlQuery
273
289
  );
290
+ window.addEventListener("pageshow", this._boundOnPageShow);
274
291
  }
275
292
 
276
293
  disconnectedCallback(): void {
@@ -282,6 +299,22 @@ export default class AmfReference extends LightningElement {
282
299
  "popstate",
283
300
  this._boundUpdateSelectedItemFromUrlQuery
284
301
  );
302
+ window.removeEventListener("pageshow", this._boundOnPageShow);
303
+ }
304
+
305
+ /**
306
+ * On bfcache restore, reset the sidebar selection so the tree re-syncs
307
+ * its highlighted tile with the current URL.
308
+ */
309
+ protected onPageShow(event: PageTransitionEvent): void {
310
+ if (!event.persisted) {
311
+ return;
312
+ }
313
+ const currentPath = window.location.pathname;
314
+ this.selectedSidebarValue = "";
315
+ Promise.resolve().then(() => {
316
+ this.selectedSidebarValue = currentPath;
317
+ });
285
318
  }
286
319
 
287
320
  renderedCallback(): void {
@@ -500,32 +533,32 @@ export default class AmfReference extends LightningElement {
500
533
  for (const [index, amfConfig] of this._amfConfigList.entries()) {
501
534
  let navItemChildren = [] as ParsedMarkdownTopic[];
502
535
  let isChildrenLoading = false;
503
- if (amfConfig.renderWith === RENDER_WITH.redoc) {
504
- // Redoc-rendered specs have no AMF model and no sub-tree; they
505
- // appear as leaf items in the sidebar and open the Redoc UI on
506
- // selection.
507
- } else if (amfConfig.referenceType !== REFERENCE_TYPES.markdown) {
508
- if (amfConfig.isSelected) {
509
- const amfPromise = this.fetchAmf(amfConfig).then(
510
- (amfJson) => {
511
- this.updateModel(amfConfig.id, amfJson);
512
- this.assignNavigationItemsFromAmf(amfConfig, index);
513
- }
514
- );
515
- this.amfFetchPromiseMap[amfConfig.id] = amfPromise;
516
- }
517
- isChildrenLoading = true;
518
- } else {
519
- const isExpandChildrenEnabled = this.isExpandChildrenEnabled(
520
- amfConfig.id
521
- );
522
- // check whether we should expand all the child nodes, this is required for Coveo to crawl.
523
- if (isExpandChildrenEnabled) {
524
- this.expandChildrenForMarkdownReferences(
525
- amfConfig.topic!.children
526
- );
536
+ if (amfConfig.renderWith !== RENDER_WITH.redoc) {
537
+ if (amfConfig.referenceType !== REFERENCE_TYPES.markdown) {
538
+ if (amfConfig.isSelected) {
539
+ const amfPromise = this.fetchAmf(amfConfig).then(
540
+ (amfJson) => {
541
+ this.updateModel(amfConfig.id, amfJson);
542
+ this.assignNavigationItemsFromAmf(
543
+ amfConfig,
544
+ index
545
+ );
546
+ }
547
+ );
548
+ this.amfFetchPromiseMap[amfConfig.id] = amfPromise;
549
+ }
550
+ isChildrenLoading = true;
551
+ } else {
552
+ const isExpandChildrenEnabled =
553
+ this.isExpandChildrenEnabled(amfConfig.id);
554
+ // check whether we should expand all the child nodes, this is required for Coveo to crawl.
555
+ if (isExpandChildrenEnabled) {
556
+ this.expandChildrenForMarkdownReferences(
557
+ amfConfig.topic!.children
558
+ );
559
+ }
560
+ navItemChildren = amfConfig.topic!.children;
527
561
  }
528
- navItemChildren = amfConfig.topic!.children;
529
562
  }
530
563
  // store nav items for each spec in order
531
564
  navAmfOrder[index] = {
@@ -535,7 +568,8 @@ export default class AmfReference extends LightningElement {
535
568
  amfConfig.isSelected ||
536
569
  this.isExpandChildrenEnabled(amfConfig.id),
537
570
  children: navItemChildren,
538
- isChildrenLoading
571
+ isChildrenLoading,
572
+ renderWith: amfConfig.renderWith
539
573
  };
540
574
  this.parentReferenceUrls.push(amfConfig.href);
541
575
  }
@@ -80,15 +80,12 @@ export interface AmfConfig {
80
80
  topic?: ParsedMarkdownTopic;
81
81
 
82
82
  /**
83
- * Optional renderer override sent by the backend.
84
- * When "redoc", spec-based references are rendered with Redoc instead of
85
- * the AMF-based topic view. Other values fall back to the default pipeline.
83
+ * Required for rendering the arrow on LNB
86
84
  */
87
85
  renderWith?: string;
88
86
 
89
87
  /**
90
- * Spec URL consumed by alternate renderers (e.g. Redoc). For redoc-rendered
91
- * references this is the OpenAPI document URL.
88
+ * Spec URL consumed by alternate renderers (e.g. Redoc, Mulesoft).
92
89
  */
93
90
  source?: string;
94
91
  }
@@ -5,27 +5,3 @@
5
5
  var(--dx-g-spacing-xl)
6
6
  );
7
7
  }
8
-
9
- .redoc-project-header {
10
- display: flex;
11
- flex-direction: column;
12
- gap: var(--dx-g-spacing-2xs);
13
- padding: var(--dx-g-spacing-s) var(--dx-g-spacing-m);
14
- }
15
-
16
- .redoc-project-back {
17
- display: inline-flex;
18
- align-items: center;
19
- gap: var(--dx-g-spacing-xs);
20
- color: var(--dx-g-blue-vibrant-50);
21
- text-decoration: none;
22
- width: fit-content;
23
- }
24
-
25
- .redoc-project-back:hover .redoc-project-title {
26
- text-decoration: underline;
27
- }
28
-
29
- .redoc-spec-title {
30
- margin: 0;
31
- }
@@ -8,22 +8,6 @@
8
8
  ></dx-error>
9
9
  </template>
10
10
  <template lwc:else>
11
- <div lwc:if={showRedocHeader} class="redoc-project-header">
12
- <a
13
- lwc:if={projectTitle}
14
- class="redoc-project-back"
15
- href="#"
16
- onclick={onBackClick}
17
- >
18
- <dx-icon sprite="utility" symbol="back" size="small"></dx-icon>
19
- <span class="redoc-project-title dx-text-body-4">
20
- {projectTitle}
21
- </span>
22
- </a>
23
- <h2 lwc:if={specTitle} class="redoc-spec-title dx-text-display-6">
24
- {specTitle}
25
- </h2>
26
- </div>
27
11
  <slot></slot>
28
12
  </template>
29
13
  </template>
@@ -2,6 +2,7 @@
2
2
  import { createElement, LightningElement, api } from "lwc";
3
3
  import DocPhase from "doc/phase";
4
4
  import DxFooter from "dx/footer";
5
+ import DxIcon from "dx/icon";
5
6
  import SprigSurvey from "doc/sprigSurvey";
6
7
  import { throttle } from "throttle-debounce";
7
8
  import { pollUntil } from "dxUtils/async";
@@ -14,11 +15,19 @@ declare global {
14
15
 
15
16
  declare const Sprig: (eventType: string, eventName: string) => void;
16
17
 
18
+ type ReferenceTopic = {
19
+ link?: { href?: string };
20
+ children?: ReferenceTopic[];
21
+ };
22
+
17
23
  type ReferenceItem = {
18
24
  source: string;
19
25
  href: string;
26
+ title?: string;
20
27
  isSelected?: boolean;
21
28
  docPhase?: string | null;
29
+ referenceType?: string;
30
+ topic?: ReferenceTopic;
22
31
  };
23
32
 
24
33
  type ReferenceConfig = {
@@ -77,21 +86,34 @@ export default class RedocReference extends LightningElement {
77
86
  */
78
87
  @api projectTitle: string | null = null;
79
88
 
89
+ private _specTitle: string | null = null;
90
+
80
91
  /** Title of the currently selected spec, shown beneath the project title. */
81
- @api specTitle: string | null = null;
92
+ @api
93
+ get specTitle(): string | null {
94
+ return this._specTitle;
95
+ }
82
96
 
97
+ set specTitle(value: string | null) {
98
+ this._specTitle = value || this.getSelectedReference()?.title || null;
99
+ }
100
+
101
+ /**
102
+ * Whether to show the project header (only for multi-spec reference sets).
103
+ */
83
104
  get showRedocHeader(): boolean {
84
- return !!(this.projectTitle || this.specTitle);
105
+ const refCount = this._referenceConfig?.refList?.length ?? 0;
106
+ const isMultiSpecSet = refCount > 1;
107
+ return isMultiSpecSet && !!(this.projectTitle || this.specTitle);
85
108
  }
86
109
 
87
110
  /**
88
- * Navigates back to the previous history entry when the user clicks the
89
- * project-title back link rendered above the Redoc UI.
111
+ * Navigates back to reference doc.
90
112
  */
91
- private onBackClick(event: Event): void {
113
+ private onBackClick = (event: Event): void => {
92
114
  event.preventDefault();
93
115
  window.history.back();
94
- }
116
+ };
95
117
 
96
118
  /** When origin is provided, pass it to the footer; otherwise use dx-footer's default. */
97
119
  get effectiveFooterOrigin(): string {
@@ -332,6 +354,9 @@ export default class RedocReference extends LightningElement {
332
354
 
333
355
  this.appendFooterItems(apiContentDiv);
334
356
 
357
+ // Inject the multi-spec project header into Redoc's left menu only.
358
+ this.insertProjectHeaderInMenu(redocContainer);
359
+
335
360
  // Wait for footer to be rendered before updating styles
336
361
  requestAnimationFrame(() => {
337
362
  this.updateRedocThirdColumnStyle(redocContainer);
@@ -344,6 +369,74 @@ export default class RedocReference extends LightningElement {
344
369
  }
345
370
  }
346
371
 
372
+ /**
373
+ * Inserts the project header into Redoc for multi-spec reference sets.
374
+ */
375
+ private insertProjectHeaderInMenu(redocContainer: HTMLElement): void {
376
+ if (!this.showRedocHeader) {
377
+ return;
378
+ }
379
+
380
+ for (const selector of [".menu-content", ".api-content"]) {
381
+ const target = redocContainer.querySelector<HTMLElement>(selector);
382
+ if (
383
+ !target ||
384
+ target.querySelector(":scope > .redoc-project-header")
385
+ ) {
386
+ continue;
387
+ }
388
+ target.insertBefore(
389
+ this.buildProjectHeaderDom(),
390
+ target.firstChild
391
+ );
392
+ }
393
+ }
394
+
395
+ /**
396
+ * Builds a fresh project-title/spec-title header DOM node.
397
+ */
398
+ private buildProjectHeaderDom(): HTMLElement {
399
+ const wrapper = document.createElement("div");
400
+ wrapper.className = "redoc-project-header";
401
+
402
+ /* TODO:
403
+ * Uncomment when doc-header is dropped with Vision state UX.
404
+ * if(this.projectTitle) {
405
+ */
406
+ if (this.specTitle) {
407
+ const backLink = document.createElement("a");
408
+ backLink.className = "redoc-project-back";
409
+ backLink.href = "#";
410
+ backLink.addEventListener("click", this.onBackClick);
411
+
412
+ const icon = createElement("dx-icon", { is: DxIcon });
413
+ Object.assign(icon, {
414
+ sprite: "utility",
415
+ symbol: "back",
416
+ size: "medium"
417
+ });
418
+ icon.classList.add("redoc-project-back-arrow");
419
+
420
+ const label = document.createElement("span");
421
+ label.className = "redoc-project-title";
422
+ label.textContent = this.specTitle;
423
+
424
+ backLink.appendChild(icon);
425
+ backLink.appendChild(label);
426
+ wrapper.appendChild(backLink);
427
+ }
428
+
429
+ // TODO: Uncomment when we we have vision state UX with no doc-header
430
+ // if (this.specTitle) {
431
+ // const specEl = document.createElement("h2");
432
+ // specEl.className = "redoc-spec-title";
433
+ // specEl.textContent = this.specTitle;
434
+ // wrapper.appendChild(specEl);
435
+ // }
436
+
437
+ return wrapper;
438
+ }
439
+
347
440
  // Waits for Redoc's API content element to be rendered
348
441
  private async waitForApiContent(
349
442
  container: HTMLElement