Skip to content

Commit

Permalink
feat: dynamically creating the sidebar
Browse files Browse the repository at this point in the history
zstix committed May 12, 2020

Unverified

This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
1 parent 8342979 commit f5e159c
Showing 3 changed files with 125 additions and 52 deletions.
10 changes: 7 additions & 3 deletions src/components/Sidebar.js
Original file line number Diff line number Diff line change
@@ -9,9 +9,13 @@ import './Sidebar.scss';
// recursively create navigation
const renderNav = (page, index) => (
<li key={index}>
<Link to={page.url} className={cx({ 'is-active': page.active })}>
{page.displayName}
</Link>
{page.url ? (
<Link to={page.url} className={cx({ 'is-active': page.active })}>
{page.displayName}
</Link>
) : (
<div>{page.displayName}</div>
)}
{page.children && <ul>{page.children.map(renderNav)}</ul>}
</li>
);
67 changes: 18 additions & 49 deletions src/pages/reference.js
Original file line number Diff line number Diff line change
@@ -1,64 +1,33 @@
import React, { useState } from 'react';
import { useStaticQuery, graphql } from 'gatsby';

import Container from '../components/Container';
import Layout from '../components/Layout';
import Sidebar from '../components/Sidebar';
import navFromEdges from '../utils/nav-from-edges';

// TODO: move this js file to same directory and update import
import '../templates/Reference.scss';

// TODO: pull this in from Gatsby
const pages = [
{ displayName: 'Overview', url: '' },
{
displayName: 'CLI',
url: '',
children: [
{ displayName: 'newrelic', url: '' },
{ displayName: 'nr1', url: '' },
],
},
{ displayName: 'GraphQL', url: '' },
{
displayName: 'Applications',
url: '',
children: [
{ displayName: 'Component Library', url: '', active: true },
{ displayName: 'File structure', url: '' },
],
},
{
displayName: 'Data Collectors',
url: '',
children: [
{ displayName: 'Custom Attributes', url: '' },
{ displayName: 'Custom Events', url: '' },
{ displayName: 'Open Telemetry', url: '' },
{ displayName: 'Telemetry SDK', url: '' },
],
},
{
displayName: 'Automation',
url: '',
children: [
{ displayName: 'Cloud Formation Provider', url: '' },
{ displayName: 'Terraform Provider', url: '' },
{
displayName: 'Agent Deploy',
url: '',
children: [
{ displayName: 'Ansible', url: '' },
{ displayName: 'Chef', url: '' },
{ displayName: 'Puppet', url: '' },
],
},
],
},
];

const Reference = () => {
const [isOpen, setIsOpen] = useState(false);

const data = useStaticQuery(graphql`
query {
allMarkdownRemark(limit: 1000) {
edges {
node {
frontmatter {
path
title
}
}
}
}
}
`);
const pages = navFromEdges(data.allMarkdownRemark.edges);

return (
<Layout>
<Container className="ReferenceTemplate">
100 changes: 100 additions & 0 deletions src/utils/nav-from-edges.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
* Convert an object used for generating the tree into an object
* to be used in the UI.
*
* @param {Object} link - A link object that includes directories.
* @param {string} link.url - The local path to the page.
* @param {string} link.displayName - The title for the page.
* @return {Object} A link object with just a url and displayName.
*/
const linkWithoutDirs = (link) => ({
url: link.url,
displayName: link.displayName,
});

/**
* Creates a link object (including a list of directories) from an
* edge (generated by GraphQL).
*
* @param {Object} edge - A markdown edge.
* @return {Object} A link object that includes directories.
*/
const linkFromEdge = (edge) => {
const { path, title } = edge?.node?.frontmatter;
return {
url: path,
displayName: title,
dirs: path.split('/').slice(1),
};
};

/**
* Converts a slug into a displayName.
*
* @param {string} str - A slug-like string.
* @return {string} A displayName for use in the UI.
*/
const makeDisplayName = (str) =>
str
.split('-')
.map((word) => word.replace(/\b\w/g, (l) => l.toUpperCase()))
.join(' ');

/**
* Generates an array of links for the UI. Each link can have an
* array of links below that. This function can call itself.
*
* TODO: do we need result?
*
* @param {Object[]} links - An array of links that are >= this level.
* @param {Object[]} [result] - The links to be returned.
* @param {number} [level=0] The nested level.
*/
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) => {
const linksUnderDir = links.filter((link) => link.dirs[level] === dir);

// find the index page, or make a non-link item for this
const index = linksUnderDir.find(
(link) => link.dirs[level + 1] === 'index'
);

// get the children for this node
const childLinks = linksUnderDir.filter((link) => link !== index);

return [
...acc,
{
...(index
? linkWithoutDirs(index)
: { displayName: makeDisplayName(dir) }),
children: genTree(childLinks, result, level + 1),
},
];
}, []);
};

/**
* Given a list of edges, generates the navigation to be used in the UI.
*
* @param {Object[]} edges - An array of edge objects.
* @return {Object[]} An array of link objects.
*/
const getNavFromEdges = (edges) => {
const links = edges.map(linkFromEdge);
return genTree(links);
};

export default getNavFromEdges;

0 comments on commit f5e159c

Please sign in to comment.