From 7481670741a41a06b98bedcf463b6d578b0bc547 Mon Sep 17 00:00:00 2001 From: Jason Maurer Date: Tue, 5 Jun 2018 12:21:55 -0400 Subject: [PATCH 1/4] added breadcrumbs to manifest and theme --- src/core/database.js | 7 +++--- src/core/hydrate.js | 4 ++++ src/core/output.js | 1 + tests/manifest.test.js | 13 +++++++++++ themes/default/breadcrumb/index.js | 36 +++++++++++++++++++++++++++++ themes/default/breadcrumb/styles.js | 31 +++++++++++++++++++++++++ themes/default/page/index.js | 3 +++ themes/default/search/index.js | 34 ++++++++++++--------------- 8 files changed, 106 insertions(+), 23 deletions(-) create mode 100644 themes/default/breadcrumb/index.js create mode 100644 themes/default/breadcrumb/styles.js diff --git a/src/core/database.js b/src/core/database.js index df20012..82edb53 100644 --- a/src/core/database.js +++ b/src/core/database.js @@ -3,20 +3,19 @@ const { getContent } = require('./filesystem') async function generateDatabase (manifest) { const db = [] - const _recursive = async ({ items, ...item }, bc = []) => { + const _recursive = async ({ items, ...item }) => { if (item.input) { db.push({ url: item.url, - breadcrumb: bc, title: item.title, + breadcrumb: item.breadcrumb, content: await getContent(item.input), }) } if (items) { - const breadcrumb = bc.concat(item.title) await Promise.all( - items.map(i => _recursive(i, breadcrumb)) + items.map(i => _recursive(i)) ) } } diff --git a/src/core/hydrate.js b/src/core/hydrate.js index 37d1bb2..d9b294b 100644 --- a/src/core/hydrate.js +++ b/src/core/hydrate.js @@ -140,6 +140,10 @@ async function hydrateTree (tree, config, onRegenerate) { throw new Error(`Duplicated URL was found: ${duplicated.join('\n\t- ')}`) } + // continue the breadcrumb from parent + hydratedItem.breadcrumb = (itemParent.breadcrumb || []) + .concat({ title: hydratedItem.title, url: hydratedItem.url }) + // pull in source items if one exists if (metaData.source) { const source = await walkSource(config.temp, hoistedItem.path, metaData) diff --git a/src/core/output.js b/src/core/output.js index f25152d..bd11ddd 100644 --- a/src/core/output.js +++ b/src/core/output.js @@ -27,6 +27,7 @@ module.exports = async (entrypoints, props) => { await fs.outputFile(outputHtml, template) await fs.outputJson(outputJson, { + title: item.title, content: item.content, }) } diff --git a/tests/manifest.test.js b/tests/manifest.test.js index 2ec2068..f98541a 100644 --- a/tests/manifest.test.js +++ b/tests/manifest.test.js @@ -19,6 +19,9 @@ describe('integration: manifest', () => { expect(res.toc.folder[0].title).to.equal('The Foo') expect(res.toc.folder[0].description).to.equal('This is a test') expect(res.toc.folder[0].url).to.equal('/foo/') + expect(res.breadcrumb).to.have.length(1) + expect(res.breadcrumb[0].title).to.equal('Mock') + expect(res.breadcrumb[0].url).to.equal('/') expect(res.items).to.have.length(7) expect(res.items[0].path).to.equal('foo') expect(res.items[0].title).to.equal('The Foo') @@ -26,6 +29,11 @@ describe('integration: manifest', () => { expect(res.items[0].url).to.equal('/foo/') expect(res.items[0].input).to.equal(syspath.resolve(__dirname, 'mock/foo/index.md')) expect(res.items[0].outputDir).to.equal('.gitdocs_build/foo/') + expect(res.items[0].breadcrumb).to.have.length(2) + expect(res.items[0].breadcrumb[0].title).to.equal('Mock') + expect(res.items[0].breadcrumb[0].url).to.equal('/') + expect(res.items[0].breadcrumb[1].title).to.equal('The Foo') + expect(res.items[0].breadcrumb[1].url).to.equal('/foo/') expect(res.items[0].items).to.have.length(3) expect(res.items[1].title).to.equal('Garply') expect(res.items[1].items[1].draft).to.be.true() @@ -38,6 +46,11 @@ describe('integration: manifest', () => { expect(res.items[4].url).to.equal('/gitdocs/') expect(res.items[4].input).to.match(/\/@repos\/gitdocs/) expect(res.items[4].outputDir).to.equal('.gitdocs_build/gitdocs/') + expect(res.items[4].breadcrumb).to.have.length(2) + expect(res.items[4].breadcrumb[0].title).to.equal('Mock') + expect(res.items[4].breadcrumb[0].url).to.equal('/') + expect(res.items[4].breadcrumb[1].title).to.equal('GitDocs') + expect(res.items[4].breadcrumb[1].url).to.equal('/gitdocs/') expect(res.items[4].items).to.have.length(1) expect(res.items[4].items[0].path).to.equal('externals.md') expect(res.items[4].items[0].title).to.equal('Externals') diff --git a/themes/default/breadcrumb/index.js b/themes/default/breadcrumb/index.js new file mode 100644 index 0000000..4601af4 --- /dev/null +++ b/themes/default/breadcrumb/index.js @@ -0,0 +1,36 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { + Wrapper, + CrumbWrapper, + Crumb, + Seperator, +} from './styles' + +const Breadcrumb = (props) => { + // don't show breadcrumbs if there is only one item + if (props.items.length < 2) { + return
+ } + + return ( + + {props.items.map((item, i) => item.url && ( + + {i > 0 && } + {item.title} + + ))} + + ) +} + +Breadcrumb.propTypes = { + items: PropTypes.array, +} + +Breadcrumb.defaultProps = { + items: [], +} + +export default Breadcrumb diff --git a/themes/default/breadcrumb/styles.js b/themes/default/breadcrumb/styles.js new file mode 100644 index 0000000..c27eea2 --- /dev/null +++ b/themes/default/breadcrumb/styles.js @@ -0,0 +1,31 @@ +import styled from 'react-emotion' +import { Link } from 'react-router-dom' +import { ChevronRight } from 'react-feather' + +export const Wrapper = styled('nav')` + margin-bottom: 20px; +` + +export const CrumbWrapper = styled('div')` + display: inline-block; +` + +export const Crumb = styled(Link)` + color: #848B8E; + font-weight: 600; + font-size: 1rem; + text-decoration: none; + opacity: .5; + transition: opacity .1s; + &:hover { + opacity: 1; + } +` + +export const Seperator = styled(ChevronRight)` + display: inline-block; + opacity: .2; + padding: 0 5px; + position: relative; + top: 2px; +` diff --git a/themes/default/page/index.js b/themes/default/page/index.js index e4984c7..ec485d9 100644 --- a/themes/default/page/index.js +++ b/themes/default/page/index.js @@ -2,6 +2,7 @@ import React, { Component } from 'react' import Helmet from 'react-helmet' import axios from 'axios' import Markdown from '../markdown' +import Breadcrumb from '../breadcrumb' import Loading from '../loading' import TocPage from '../toc/page' import TocFolder from '../toc/folder' @@ -90,6 +91,8 @@ export default class Page extends Component { {route.title} + + {loading ? : ( diff --git a/themes/default/search/index.js b/themes/default/search/index.js index b8e24ac..4c7f198 100644 --- a/themes/default/search/index.js +++ b/themes/default/search/index.js @@ -151,25 +151,21 @@ class Search extends Component { } renderBreadCrumb (result) { - return result - .breadcrumb + return result.breadcrumb .slice(1, result.breadcrumb.length) - .concat(result.title) - .map((b, i) => ( - - { - i !== 0 && - - } - {b} + .map(({ title }, i) => ( + + {i !== 0 && } + + {title} )) } @@ -181,7 +177,7 @@ class Search extends Component { // Map over search results and create links const items = results.map((r, i) => i === selectedIndex ? this.activeItem = ref : null} onClick={this.clearSearch} From 382ca8321d50c1197676c53a30562df7d071cd1a Mon Sep 17 00:00:00 2001 From: Jason Maurer Date: Tue, 5 Jun 2018 12:24:17 -0400 Subject: [PATCH 2/4] disable breadcrumbs from config --- src/utils/config.js | 1 + themes/default/page/index.js | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/utils/config.js b/src/utils/config.js index 1514574..d9e0dad 100644 --- a/src/utils/config.js +++ b/src/utils/config.js @@ -28,6 +28,7 @@ const DEFAULT_CONFIG = { languages: ['bash', 'json'], header_links: [], theme: 'default', + breadcrumbs: true, prefix_titles: false, table_of_contents: { page: true, diff --git a/themes/default/page/index.js b/themes/default/page/index.js index ec485d9..d404eb4 100644 --- a/themes/default/page/index.js +++ b/themes/default/page/index.js @@ -91,7 +91,8 @@ export default class Page extends Component { {route.title} - + {config.breadcrumbs && + } {loading ? From c06491a774e311e6ae228e631792019a72b3825d Mon Sep 17 00:00:00 2001 From: Jason Maurer Date: Tue, 5 Jun 2018 12:37:02 -0400 Subject: [PATCH 3/4] disable breadcrumbs from front matter --- src/core/hydrate.js | 6 ++++-- src/utils/config.js | 2 +- themes/default/page/index.js | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/core/hydrate.js b/src/core/hydrate.js index d9b294b..a53fb1e 100644 --- a/src/core/hydrate.js +++ b/src/core/hydrate.js @@ -141,8 +141,10 @@ async function hydrateTree (tree, config, onRegenerate) { } // continue the breadcrumb from parent - hydratedItem.breadcrumb = (itemParent.breadcrumb || []) - .concat({ title: hydratedItem.title, url: hydratedItem.url }) + if (config.breadcrumb && metaData.breadcrumb !== false) { + hydratedItem.breadcrumb = (itemParent.breadcrumb || []) + .concat({ title: hydratedItem.title, url: hydratedItem.url }) + } // pull in source items if one exists if (metaData.source) { diff --git a/src/utils/config.js b/src/utils/config.js index d9e0dad..e91fc2f 100644 --- a/src/utils/config.js +++ b/src/utils/config.js @@ -28,7 +28,7 @@ const DEFAULT_CONFIG = { languages: ['bash', 'json'], header_links: [], theme: 'default', - breadcrumbs: true, + breadcrumb: true, prefix_titles: false, table_of_contents: { page: true, diff --git a/themes/default/page/index.js b/themes/default/page/index.js index d404eb4..5515486 100644 --- a/themes/default/page/index.js +++ b/themes/default/page/index.js @@ -91,7 +91,7 @@ export default class Page extends Component { {route.title} - {config.breadcrumbs && + {route.breadcrumb && } {loading From 34edc01078ba45b8e8781d2739aedc8fa5756256 Mon Sep 17 00:00:00 2001 From: Jason Maurer Date: Tue, 5 Jun 2018 12:52:36 -0400 Subject: [PATCH 4/4] fix breadcrumbs with duplicated urls --- src/core/hydrate.js | 14 ++++++++-- src/utils/config.js | 2 +- tests/manifest.test.js | 27 ++++++++++--------- tests/mock/qux/corge.md | 3 +++ .../{breadcrumb => breadcrumbs}/index.js | 10 +++---- .../{breadcrumb => breadcrumbs}/styles.js | 0 themes/default/page/index.js | 6 ++--- 7 files changed, 38 insertions(+), 24 deletions(-) rename themes/default/{breadcrumb => breadcrumbs}/index.js (75%) rename themes/default/{breadcrumb => breadcrumbs}/styles.js (100%) diff --git a/src/core/hydrate.js b/src/core/hydrate.js index a53fb1e..9e70f67 100644 --- a/src/core/hydrate.js +++ b/src/core/hydrate.js @@ -141,9 +141,19 @@ async function hydrateTree (tree, config, onRegenerate) { } // continue the breadcrumb from parent - if (config.breadcrumb && metaData.breadcrumb !== false) { - hydratedItem.breadcrumb = (itemParent.breadcrumb || []) + if (config.breadcrumbs && metaData.breadcrumbs !== false) { + const breadcrumbs = [] + const breadcrumbsParent = itemParent.breadcrumbs || [] + + breadcrumbsParent .concat({ title: hydratedItem.title, url: hydratedItem.url }) + // only add unique urls to the breadcrumb + .forEach(crumb => + breadcrumbs.findIndex(i => i.url === crumb.url) === -1 && + breadcrumbs.push(crumb) + ) + + hydratedItem.breadcrumbs = breadcrumbs } // pull in source items if one exists diff --git a/src/utils/config.js b/src/utils/config.js index e91fc2f..d9e0dad 100644 --- a/src/utils/config.js +++ b/src/utils/config.js @@ -28,7 +28,7 @@ const DEFAULT_CONFIG = { languages: ['bash', 'json'], header_links: [], theme: 'default', - breadcrumb: true, + breadcrumbs: true, prefix_titles: false, table_of_contents: { page: true, diff --git a/tests/manifest.test.js b/tests/manifest.test.js index f98541a..fca32ce 100644 --- a/tests/manifest.test.js +++ b/tests/manifest.test.js @@ -19,9 +19,9 @@ describe('integration: manifest', () => { expect(res.toc.folder[0].title).to.equal('The Foo') expect(res.toc.folder[0].description).to.equal('This is a test') expect(res.toc.folder[0].url).to.equal('/foo/') - expect(res.breadcrumb).to.have.length(1) - expect(res.breadcrumb[0].title).to.equal('Mock') - expect(res.breadcrumb[0].url).to.equal('/') + expect(res.breadcrumbs).to.have.length(1) + expect(res.breadcrumbs[0].title).to.equal('Mock') + expect(res.breadcrumbs[0].url).to.equal('/') expect(res.items).to.have.length(7) expect(res.items[0].path).to.equal('foo') expect(res.items[0].title).to.equal('The Foo') @@ -29,11 +29,11 @@ describe('integration: manifest', () => { expect(res.items[0].url).to.equal('/foo/') expect(res.items[0].input).to.equal(syspath.resolve(__dirname, 'mock/foo/index.md')) expect(res.items[0].outputDir).to.equal('.gitdocs_build/foo/') - expect(res.items[0].breadcrumb).to.have.length(2) - expect(res.items[0].breadcrumb[0].title).to.equal('Mock') - expect(res.items[0].breadcrumb[0].url).to.equal('/') - expect(res.items[0].breadcrumb[1].title).to.equal('The Foo') - expect(res.items[0].breadcrumb[1].url).to.equal('/foo/') + expect(res.items[0].breadcrumbs).to.have.length(2) + expect(res.items[0].breadcrumbs[0].title).to.equal('Mock') + expect(res.items[0].breadcrumbs[0].url).to.equal('/') + expect(res.items[0].breadcrumbs[1].title).to.equal('The Foo') + expect(res.items[0].breadcrumbs[1].url).to.equal('/foo/') expect(res.items[0].items).to.have.length(3) expect(res.items[1].title).to.equal('Garply') expect(res.items[1].items[1].draft).to.be.true() @@ -46,16 +46,17 @@ describe('integration: manifest', () => { expect(res.items[4].url).to.equal('/gitdocs/') expect(res.items[4].input).to.match(/\/@repos\/gitdocs/) expect(res.items[4].outputDir).to.equal('.gitdocs_build/gitdocs/') - expect(res.items[4].breadcrumb).to.have.length(2) - expect(res.items[4].breadcrumb[0].title).to.equal('Mock') - expect(res.items[4].breadcrumb[0].url).to.equal('/') - expect(res.items[4].breadcrumb[1].title).to.equal('GitDocs') - expect(res.items[4].breadcrumb[1].url).to.equal('/gitdocs/') + expect(res.items[4].breadcrumbs).to.have.length(2) + expect(res.items[4].breadcrumbs[0].title).to.equal('Mock') + expect(res.items[4].breadcrumbs[0].url).to.equal('/') + expect(res.items[4].breadcrumbs[1].title).to.equal('GitDocs') + expect(res.items[4].breadcrumbs[1].url).to.equal('/gitdocs/') expect(res.items[4].items).to.have.length(1) expect(res.items[4].items[0].path).to.equal('externals.md') expect(res.items[4].items[0].title).to.equal('Externals') expect(res.items[4].items[0].url).to.equal('/gitdocs/externals/') expect(res.items[5].component).to.equal('Divider') expect(res.items[6].title).to.equal('The Quux') + expect(res.items[6].items[1].breadcrumbs).to.be.undefined() }) }) diff --git a/tests/mock/qux/corge.md b/tests/mock/qux/corge.md index 75309bf..5f4f05b 100644 --- a/tests/mock/qux/corge.md +++ b/tests/mock/qux/corge.md @@ -1 +1,4 @@ +--- +breadcrumbs: false +--- # The Corge diff --git a/themes/default/breadcrumb/index.js b/themes/default/breadcrumbs/index.js similarity index 75% rename from themes/default/breadcrumb/index.js rename to themes/default/breadcrumbs/index.js index 4601af4..a51bc03 100644 --- a/themes/default/breadcrumb/index.js +++ b/themes/default/breadcrumbs/index.js @@ -7,7 +7,7 @@ import { Seperator, } from './styles' -const Breadcrumb = (props) => { +const Breadcrumbs = (props) => { // don't show breadcrumbs if there is only one item if (props.items.length < 2) { return
@@ -16,7 +16,7 @@ const Breadcrumb = (props) => { return ( {props.items.map((item, i) => item.url && ( - + {i > 0 && } {item.title} @@ -25,12 +25,12 @@ const Breadcrumb = (props) => { ) } -Breadcrumb.propTypes = { +Breadcrumbs.propTypes = { items: PropTypes.array, } -Breadcrumb.defaultProps = { +Breadcrumbs.defaultProps = { items: [], } -export default Breadcrumb +export default Breadcrumbs diff --git a/themes/default/breadcrumb/styles.js b/themes/default/breadcrumbs/styles.js similarity index 100% rename from themes/default/breadcrumb/styles.js rename to themes/default/breadcrumbs/styles.js diff --git a/themes/default/page/index.js b/themes/default/page/index.js index 5515486..a24b074 100644 --- a/themes/default/page/index.js +++ b/themes/default/page/index.js @@ -2,7 +2,7 @@ import React, { Component } from 'react' import Helmet from 'react-helmet' import axios from 'axios' import Markdown from '../markdown' -import Breadcrumb from '../breadcrumb' +import Breadcrumbs from '../breadcrumbs' import Loading from '../loading' import TocPage from '../toc/page' import TocFolder from '../toc/folder' @@ -91,8 +91,8 @@ export default class Page extends Component { {route.title} - {route.breadcrumb && - } + {route.breadcrumbs && + } {loading ?