Skip to content

Instantly share code, notes, and snippets.

@zstix
Last active May 12, 2020 19:04
Show Gist options
  • Save zstix/343dc9d21362bc9eb731ba59da32340a to your computer and use it in GitHub Desktop.
Save zstix/343dc9d21362bc9eb731ba59da32340a to your computer and use it in GitHub Desktop.
Generate a nested navigation from a flat list of markdown edges
const linkWithoutDirs = ({ url, displayName }) => ({ url, displayName });
const linkFromEdge = edge => ({
url: edge?.node?.frontmatter?.path,
displayName: edge?.node?.frontmatter?.title
});
const addDirsToLink = link => ({
...link,
dirs: link.url.split('/').slice(1)
});
const makeDisplayName = str => str
.split('-')
.map(word => word.replace(/\b\w/g, l => l.toUpperCase()))
.join(' ');
const genTree = (links, result = [], level = 0) => {
const linksAtLevel = links.filter(link => level === link.dirs.length - 1);
// if we have nothing below this, just return a flat list of links
if (linksAtLevel.length === links.length) {
return linksAtLevel.map(linkWithoutDirs);
}
// get all the directories at this level
const linksBelowLevel = links.filter(link => level < link.dirs.length - 1);
const dirsAtLevel = linksBelowLevel.map(link => link.dirs[level]);
const uniqueDirsAtLevel = [...new Set(dirsAtLevel)];
return uniqueDirsAtLevel.reduce((acc, dir) => {
// find the index page, or make a non-link item for this
const linksUnderDir = links.filter(link => link.dirs[level] === dir);
const index = linksUnderDir.find(link => link.dirs[level + 1] === 'index');
const childLinks = linksUnderDir.filter(link => link !== index);
return [
...acc,
{
...(index ? linkWithoutDirs(index) : { displayName: makeDisplayName(dir) }),
children: genTree(childLinks, result, level + 1)
}
]
}, []);
};
const genNav = (edges) => {
const links = edges.map(linkFromEdge);
const linksWithDirs = links.map(addDirsToLink);
const nav = genTree(linksWithDirs);
// TODO: sorting?
return nav;
}
@zstix
Copy link
Author

zstix commented May 12, 2020

A solution to building out nested links for our sidebar navigation from a flat list of edges coming from Gatsby's GraphQL instance. This is not very performant, but it does work. With some additional effort, we can probably reduce the number of loops.

Input

[
  {
    "node": {
      "frontmatter": {
        "title": "New Relic Guides",
        "path": "/guides/index"
      }
    }
  },
  {
    "node": {
      "frontmatter": {
        "title": "GraphQL API",
        "path": "/guides/graphql-api"
      }
    }
  },
  {
    "node": {
      "frontmatter": {
        "title": "Pumpkin: Dolor",
        "path": "/lorem/ipsum/dolor"
      }
    }
  },
  {
    "node": {
      "frontmatter": {
        "title": "Salmon: Amet",
        "path": "/lorem/ipsum/amet"
      }
    }
  }
]

Output

[
  {
    "url": "/guides/index",
    "displayName": "New Relic Guides",
    "children": [
      {
        "url": "/guides/graphql-api",
        "displayName": "GraphQL API"
      }
    ]
  },
  {
    "displayName": "Lorem",
    "children": [
      {
        "displayName": "Ipsum",
        "children": [
          {
            "url": "/lorem/ipsum/dolor",
            "displayName": "Pumpkin: Dolor"
          },
          {
            "url": "/lorem/ipsum/amet",
            "displayName": "Salmon: Amet"
          }
        ]
      }
    ]
  }
]

@zstix
Copy link
Author

zstix commented May 12, 2020

This work has been cleaned up and submitted in a PR here: newrelic/developer-website#50

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment