elements icon indicating copy to clipboard operation
elements copied to clipboard

Anchor links from articles not working in Elements Dev Portal

Open dylangarrett opened this issue 1 year ago • 0 comments

The anchor links from any articles written in the Stoplight platform do not work correctly in Angular. The basePath value from the elements component is not considered in the anchor link.

Similar to https://github.com/stoplightio/elements/issues/1699 and https://github.com/stoplightio/elements/issues/2133

Context

  • Consider docs hosted at example.com/docs and an article at example.com/docs/article
  • Any anchor links in the article will point at example.com/#anchor instead of example.com/docs/article#anchor
  • Not only is the link incorrect, it might navigate users away from the documentation

Current Behavior

  • Anchor link is incorrect
  • Article anchor links do not consider the basePath of the component
    <elements-stoplight-project
    projectId="..."
    basePath="/api/docs" // this is not reflected in anchor links 
   ></elements-stoplight-project>

Expected Behavior

  • Anchor link should be correct
  • Clicking should scroll to the correct section instead of navigating to a different page

Possible Workaround/Solution

As a workaround I directly modify anchor tags in my component, it works but shouldn't be necessary

export class ApiPortalComponent implements AfterViewInit {
  private mutationObserver: MutationObserver;
  public basePath = '/docs/api';
  private elementsQuerySelector = 'elements-stoplight-project';
  
  constructor(private router: Router, private renderer: Renderer2, private el: ElementRef) {}

  ngAfterViewInit() {
    this.updateAnchorLinks();

    this.mutationObserver = new MutationObserver(() => {
      this.updateAnchorLinks();
    });

    const targetNode = this.el.nativeElement.querySelector(this.elementsQuerySelector);
    if (targetNode) {
      this.mutationObserver.observe(targetNode, {
        childList: true,
        subtree: true,
      });
    }
  }

  ngOnDestroy() {
    if (this.mutationObserver) {
      this.mutationObserver.disconnect();
    }
  }

  /**
   * Fixes an issue where anchor links are not working as expected in Stoplight Elements Dev Portal
   * https://github.com/stoplightio/elements/issues/1699
   * https://github.com/stoplightio/elements/issues/2133
   * 
   * The anchor links do not contain the base path of the API portal, so we need to update them.
   * Loop through all anchor links in the API portal and update the href attribute to include the base path.
   * Also fixes the issue where the page refreshes when clicking on an anchor link.
   */
  public updateAnchorLinks() {
    const anchorLinks = this.el.nativeElement.querySelectorAll(`${this.elementsQuerySelector} a[href^="#"]`);
    anchorLinks.forEach((link: HTMLAnchorElement) => {
      const anchor = link.getAttribute('href')?.split('#')[1];
      if (anchor && link.href.indexOf(this.basePath) === -1) { 

        this.renderer.setAttribute(link, 'href', `${this.basePath}#${anchor}`);
        
        // Add click event listener to prevent full page refresh and scroll to the anchor
        this.renderer.listen(link, 'click', (event) => {
          event.preventDefault();
          void this.router.navigate([this.basePath], { fragment: anchor }).then(() => {
            this.scrollToAnchor(anchor);
          });
        });
      }
    });
  }

  private scrollToAnchor(anchor: string) {
    const element = document.getElementById(anchor);
    if (element) {
      element.scrollIntoView({ behavior: 'smooth' });
    }
  }
}

Steps to Reproduce

  1. Run elements dev portal locally, ensure there is a basePath specified in the component
  2. Write an article which includes headings
  3. Navigate to the article in the dev portal, click a link on the right side image

Environment

  • Version used: "@stoplight/elements-dev-portal": "2.2.0"
  • Environment name and version (e.g. Chrome 39, node.js 5.4): Chrome
  • Operating System and version (desktop or mobile): Both

dylangarrett avatar Jun 11 '24 06:06 dylangarrett