Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] task/issue-275 Create Extensible Menus Query #285

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"lit-element": "^2.0.1",
"lit-redux-router": "^0.9.3",
"local-web-server": "^2.6.1",
"markdown-toc": "^1.2.0",
"node-fetch": "^2.6.0",
"postcss-loader": "^3.0.0",
"postcss-nested": "^4.1.2",
Expand All @@ -71,4 +72,4 @@
"webpack-manifest-plugin": "^2.0.4",
"webpack-merge": "^4.2.1"
}
}
}
20 changes: 20 additions & 0 deletions packages/cli/src/data/queries/menu.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
query($menu: String!, $route: String) {
menu(filter: $menu, pathname: $route) {
item {
label,
link
}
children {
item {
label,
link
},
children {
item {
label,
link
}
}
}
}
}
6 changes: 0 additions & 6 deletions packages/cli/src/data/queries/navigation.gql

This file was deleted.

88 changes: 60 additions & 28 deletions packages/cli/src/data/schema/graph.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,48 @@
const { gql } = require('apollo-server-express');

const getMenuFromGraph = async (root, { pathname, filter = '' }, context) => {
const { graph } = context;
let items = [];

// TODO Issue #271 https://github.com/ProjectEvergreen/greenwood/issues/271
// Sort menus by label/index asc/desc
graph
.forEach((page) => {
const { route, menu, title, tableOfContents } = page;
let children = [];

if (menu && menu.search(filter) > -1) {
if (menu === 'side') {
// check we're querying only pages that contain base route
let baseRouteIndex = pathname.substring(1, pathname.length).indexOf('/');
let baseRoute = pathname.substring(0, baseRouteIndex + 1);

if (route.includes(baseRoute)) {
items.push({ item: { link: route, label: title }, children: getParsedHeadingsFromPage(tableOfContents) });
}
} else {
items.push({ item: { link: route, label: title }, children });
}
}
});

return { label: filter, link: 'na', children: items };
};

const getParsedHeadingsFromPage = (tableOfContents) => {
let children = [];

if (tableOfContents.length > 0) {
tableOfContents.forEach(({ content, slug, lvl }) => {
// make sure we only add h3 links to menu
if (lvl === 3) {
children.push({ item: { label: content, link: '#' + slug }, children: [] });
}
});
}
return children;
};

const getDeriveMetaFromRoute = (route) => {
// TODO hardcoded root / depth - #273
const root = route.split('/')[1] || '';
Expand All @@ -13,7 +56,7 @@ const getDeriveMetaFromRoute = (route) => {
return {
label,
root
};
};
};

const getPagesFromGraph = async (root, query, context) => {
Expand All @@ -39,29 +82,6 @@ const getPagesFromGraph = async (root, query, context) => {
return pages;
};

const getNavigationFromGraph = async (root, query, context) => {
const navigation = {};
const { graph } = context;

graph
.forEach((page) => {
const { route } = page;
const { root, label } = getDeriveMetaFromRoute(route);

if (root !== '' && !navigation[root]) {
navigation[root] = {
label,
link: `/${root}/`
};
}
});

// TODO best format for users, hash map? #271
return Object.keys(navigation).map((key) => {
return navigation[key];
});
};

const getChildrenFromParentRoute = async (root, query, context) => {
const pages = [];
const { parent } = query;
Expand All @@ -86,7 +106,7 @@ const getChildrenFromParentRoute = async (root, query, context) => {
});
}
});

return pages;
};

Expand All @@ -100,22 +120,34 @@ const graphTypeDefs = gql`
title: String
}

type Navigation {
type Link {
label: String,
link: String
}

type Menu {
item: Link
children: [Menu]
}

type Query {
graph: [Page]
navigation: [Navigation]
menu(filter: String, pathname: String, orderBy: MenuOrderBy): Menu
children(parent: String): [Page]
}

enum MenuOrderBy {
label_asc,
label_desc
index_asc,
index_desc
}
`;

const graphResolvers = {
Query: {
graph: getPagesFromGraph,
navigation: getNavigationFromGraph,
menu: getMenuFromGraph,
children: getChildrenFromParentRoute
}
};
Expand Down
26 changes: 23 additions & 3 deletions packages/cli/src/lifecycles/graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const fs = require('fs-extra');
const crypto = require('crypto');
const fm = require('front-matter');
const path = require('path');
const toc = require('markdown-toc');

const createGraphFromPages = async (pagesDir, config) => {
let pages = [];
Expand All @@ -23,9 +24,9 @@ const createGraphFromPages = async (pagesDir, config) => {
if (isMdFile && !stats.isDirectory()) {
const fileContents = await fs.readFile(filePath, 'utf8');
const { attributes } = fm(fileContents);
let { label, template, title } = attributes;
let { label, template, title, menu, index, linkheadings } = attributes;
let { meta } = config;
let mdFile = '';
let mdFile = '', tableOfContents = [];

// if template not set, use default
template = template || 'page';
Expand Down Expand Up @@ -65,6 +66,21 @@ const createGraphFromPages = async (pagesDir, config) => {
// set <title></title> element text, override with markdown title
title = title || config.title;

// set specific menu to place this page
menu = menu || '';

// set specific index list priority of this item within a menu
index = index || '';

// set flag whether to gather a list of headings on a page as menu items
linkheadings = linkheadings || false;

if (linkheadings) {
// parse markdown for table of contents and output to json
tableOfContents = toc(fileContents).json;
tableOfContents.shift();
}

/*
* Variable Definitions
*----------------------
Expand All @@ -77,10 +93,14 @@ const createGraphFromPages = async (pagesDir, config) => {
* relativeExpectedPath: relative import path for generated component within a list.js file to later be
* imported into app.js root component
* title: the head <title></title> text
* menu: the name of the menu in which this item can be listed and queried
* index: the index of this list item within a menu
* tableOfContents: json object containing page's table of contents(list of headings)
* meta: og graph meta array of objects { property/name, content }
*/

pages.push({ mdFile, label, route, template, filePath, fileName, relativeExpectedPath, title, meta });
pages.push({ mdFile, label, route, template, filePath, fileName, relativeExpectedPath,
title, menu, index, tableOfContents, meta });
}
if (stats.isDirectory()) {
await walkDirectory(filePath);
Expand Down
16 changes: 10 additions & 6 deletions www/components/header/header.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { LitElement, html } from 'lit-element';
import client from '@greenwood/cli/data/client';
import NavigationQuery from '@greenwood/cli/data/queries/navigation';
import MenuQuery from '@greenwood/cli/data/queries/menu';
import '@evergreen-wc/eve-container';
import headerCss from './header.css';
import brand from '../../assets/brand.png';
import '../components/social-icons/social-icons';

class HeaderComponent extends LitElement {

static get properties() {
return {
navigation: {
Expand All @@ -25,10 +25,14 @@ class HeaderComponent extends LitElement {
super.connectedCallback();

const response = await client.query({
query: NavigationQuery
query: MenuQuery,
variables: {
menu: 'navigation'
}
});

this.navigation = response.data.navigation;
this.navigation = response.data.menu.children;
console.log(this.navigation);
}

/* eslint-disable indent */
Expand All @@ -42,7 +46,7 @@ class HeaderComponent extends LitElement {
<header class="header">
<eve-container fluid>
<div class="head-wrap">

<div class="brand">
<a href="https://projectevergreen.github.io" target="_blank" rel="noopener noreferrer"
@onclick="getOutboundLink('https://projectevergreen.github.io'); return false;" >
Expand All @@ -55,7 +59,7 @@ class HeaderComponent extends LitElement {

<nav>
<ul>
${navigation.map((item) => {
${navigation.map(({ item }) => {
return html`
<li><a href="${item.link}" title="Click to visit the ${item.label} page">${item.label}</a></li>
`;
Expand Down
51 changes: 22 additions & 29 deletions www/components/shelf/shelf.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { LitElement, html } from 'lit-element';
import client from '@greenwood/cli/data/client';
import ChildrenQuery from '@greenwood/cli/data/queries/children';
import MenuQuery from '@greenwood/cli/data/queries/menu';
import css from './shelf.css';
import chevronRt from '../icons/chevron-right/chevron-right';
import chevronDwn from '../icons/chevron-down/chevron-down';
Expand All @@ -22,29 +22,24 @@ class Shelf extends LitElement {
this.page = '';
}

connectedCallback() {
async connectedCallback() {
super.connectedCallback();
this.collapseAll();
this.expandRoute(window.location.pathname);
}

async setupShelf(page) {
console.log('setupShelf for page =>', page);

if (page && page !== '' && page !== '/') {
// TODO remove require call - #275
this.shelfList = require(`./${page}.json`);

// TODO not actually integrated, still using .json files - #271
Copy link
Member Author

@hutchgrant hutchgrant Feb 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is what I'm referring to when I resolved 275 and started 271, using my method. You knew we were going to use a query here. You started using the query, but only printed it out to console and never fully implemented it. I thought the require was only temporary because the query wasn't completed. Now it is completed.

if (this.page !== '' && this.page !== '/') {
const response = await client.query({
query: ChildrenQuery,
query: MenuQuery,
variables: {
parent: page
menu: 'side',
route: window.location.pathname
}
});

console.log('response from the shelf (data.children)', response.data.children);
console.log('shelf =>', response.data.menu.children);
this.shelfList = response.data.menu.children;
this.requestUpdate();
}

this.collapseAll();
// this.expandRoute(window.location.pathname);
}

goTo(path) {
Expand Down Expand Up @@ -110,12 +105,12 @@ class Shelf extends LitElement {
const renderListItems = (list) => {
let listItems = '';

if (list.items && list.items.length > 0) {
if (list && list.length > 0) {
listItems = html`
<ul>
${list.items.map((item, index) => {
${list.map(({ item }, index) => {
return html`
<li id="index_${index}" class="${list.selected ? '' : 'hidden'}"><a @click=${()=> this.goTo(`#${item.id}`)}">${item.name}</a></li>
<li id="index_${index}" class="${item.selected ? '' : 'hidden'}"><a @click=${()=> this.goTo(`${item.link}`)}">${item.label}</a></li>
`;
})}
</ul>
Expand All @@ -124,29 +119,27 @@ class Shelf extends LitElement {

return listItems;
};

/* eslint-enable */
console.log(this.shelfList);

return this.shelfList.map((list, index) => {
return this.shelfList.map(({ item, children, selected }, index) => {
let id = `index_${index}`;
let chevron = list.items && list.items.length > 0
? list.selected === true ? chevronDwn : chevronRt
let chevron = children && children.length > 0
? selected === true ? chevronDwn : chevronRt
: '';

return html`
<li class="list-wrap">
<a href="${list.path}" @click="${this.handleClick}"><h2 id="${id}">${list.name} <span>${chevron}</span></h2></a>
<a href="${item.link}" @click="${this.handleClick}"><h2 id="${id}">${item.label} <span>${chevron}</span></h2></a>
<hr>
${renderListItems(list)}
${renderListItems(children)}
</li>
`;
});
}

render() {
const { page } = this;

this.setupShelf(page);

return html`
<style>
${css}
Expand Down
Loading