diff --git a/asset/css/markbind.css b/asset/css/markbind.css index 9dcaabe2b2..129931cbed 100644 --- a/asset/css/markbind.css +++ b/asset/css/markbind.css @@ -78,12 +78,57 @@ code.hljs.inline { background-color: #fff; } +/** + * Holy Grail Layout + * + * This section covers the common styles used within Header, Footer and Side Navigation bars + */ + +/* #app is treated as the main container */ +#app { + display: flex; + flex-direction: column; + min-height: 100vh; +} + +#flex-body { + display: flex; + flex: 1; +} + #content-wrapper { + flex: 1; margin: 0 auto; max-width: 1000px; - overflow-x: hidden; padding: 0 20px; + transition: 0.4s; width: 100%; + -webkit-transition: 0.4s; +} + +/* Footer */ +footer { + background-color: #f5f5f5; + color: dimgrey; + padding: 10px 0; +} + +/* Side Navigation menus */ + +.spacer-top { + /* + spacer-top class is a work around for MarkBind component using "position: fixed". + This class can be removed once the component's position type is changed. + */ + padding-top: 70px; +} + +.scrollable { + overflow-y: auto; +} + +.viewport-height-90 { + max-height: 90vh; } /* Bootstrap small(sm) responsive breakpoint */ @@ -111,24 +156,6 @@ code.hljs.inline { } } -/* Footer */ - -footer { - background-color: #f5f5f5; - color: dimgrey; - padding: 10px 0; -} - -#app { - display: flex; - flex-direction: column; - min-height: 100vh; -} - -#flex-div { - flex: 1; -} - /* Scrollbar */ .slim-scroll::-webkit-scrollbar { diff --git a/asset/css/page-nav.css b/asset/css/page-nav.css index c8d709fec5..2d282c78f9 100644 --- a/asset/css/page-nav.css +++ b/asset/css/page-nav.css @@ -2,11 +2,9 @@ #page-nav { border-left: solid 1px lightgrey; - display: inline-block; - max-height: 80vh; - overflow: auto; - position: fixed; - right: 0; + display: block; + flex: 0 0 auto; + max-width: 300px; transition: 0.4s; width: 300px; -webkit-transition: 0.4s; @@ -27,13 +25,20 @@ color: black; } -#page-nav-content-wrapper { - margin-right: 300px; - transition: 0.4s; - -webkit-transition: 0.4s; +.page-nav-title { + color: black; + white-space: inherit; } -/* Responsive site navigation */ +.nested { + margin-left: 5%; +} + +.no-flex-wrap { + flex-wrap: nowrap; +} + +/* Responsive page navigation */ /* Hides page navigation on screen widths smaller than 1300px */ @media screen and (max-width: 1299.98px) { @@ -44,19 +49,11 @@ padding: 0; width: 0px; } - - #page-nav-content-wrapper { - margin-right: 0; - } } /* Hide page navigation when printing */ @media print { - #page-nav-content-wrapper { - margin-right: 0; - } - #page-nav { border: none; display: none; diff --git a/asset/css/site-nav.css b/asset/css/site-nav.css index 4d030b05e8..dd47bdd833 100644 --- a/asset/css/site-nav.css +++ b/asset/css/site-nav.css @@ -1,32 +1,15 @@ /* Site navigation */ -#flex-body { - display: flex; - flex: 1; -} - -#page-content { - flex: 1; - margin-left: 300px; - overflow-x: hidden; - padding: 0; - transition: 0.4s; - -webkit-transition: 0.4s; -} - #site-nav { background: white; border-right: 1px solid lightgrey; - display: inline-block; - left: 0; - max-height: 80vh; - overflow: auto; + display: block; + flex: 0 0 auto; + max-width: 300px; + padding-bottom: 20px; padding-right: 20px; - padding-top: 20px; - position: fixed; transition: 0.4s; width: 300px; - z-index: 1; -webkit-transition: 0.4s; } @@ -73,6 +56,13 @@ -webkit-transition: 0.4s; } +/* Navigation list */ + +.site-nav-list { + list-style-type: none; + margin-left: -1em; +} + /* Navigation dropdown menu */ .dropdown-btn { @@ -159,7 +149,7 @@ #site-nav { border: none; opacity: 0; - overflow: hidden; + padding-right: 0; width: 0; } @@ -169,10 +159,6 @@ width: 60px; } - #page-content { - margin-left: 60px; - } - footer { margin-left: 60px; } @@ -208,10 +194,6 @@ width: 0; } - #page-content { - margin-left: 0; - } - footer { margin-left: 0; } @@ -231,10 +213,6 @@ /* Hide site navigation when printing */ @media print { - #page-content { - margin-left: 0; - } - #site-nav { border: none; display: none; diff --git a/asset/js/setup.js b/asset/js/setup.js index 0435c23a30..6bd65b1038 100644 --- a/asset/js/setup.js +++ b/asset/js/setup.js @@ -93,12 +93,6 @@ function setupSiteNav() { ); } -function setupPageNav() { - jQuery(window).on('activate.bs.scrollspy', (event, obj) => { - document.querySelectorAll(`a[href='${obj.relatedTarget}']`).item(0).scrollIntoView(false); - }); -} - function setup() { // eslint-disable-next-line no-unused-vars const vm = new Vue({ @@ -111,7 +105,6 @@ function setup() { }, }); setupSiteNav(); - setupPageNav(); } function setupWithSearch() { @@ -143,7 +136,6 @@ function setupWithSearch() { }, }); setupSiteNav(); - setupPageNav(); } if (enableSearch) { diff --git a/docs/userGuide/fullSyntaxReference.md b/docs/userGuide/fullSyntaxReference.md index 8d2fc4c32e..3dca0b0663 100644 --- a/docs/userGuide/fullSyntaxReference.md +++ b/docs/userGuide/fullSyntaxReference.md @@ -37,6 +37,7 @@ boxes : ['Boxes', ['component', 'reader-facing']], dropdowns : ['Dropdowns', ['component', 'reader-facing']], footers : ['Footers', ['other', 'reader-facing']], + headers: ['Headers', ['other', 'reader-facing']], modals : ['Modals', ['component', 'reader-facing']], navBars : ['Nav Bars', ['component', 'reader-facing']], pageNavigationMenus : ['Page Navigation Menus', ['component', 'reader-facing']], diff --git a/docs/userGuide/syntax/footers.mbdf b/docs/userGuide/syntax/footers.mbdf index 9aeb2dd97c..22dfed5d27 100644 --- a/docs/userGuide/syntax/footers.mbdf +++ b/docs/userGuide/syntax/footers.mbdf @@ -1,20 +1,8 @@ ## Footers -**You can specify a page footer** using a ` - @@ -69,11 +72,14 @@

Filler text - + - + + \ No newline at end of file diff --git a/test/functional/test_site/expected/testEmptyFrontmatter.html b/test/functional/test_site/expected/testEmptyFrontmatter.html index 66b9469169..7d5c73caf2 100644 --- a/test/functional/test_site/expected/testEmptyFrontmatter.html +++ b/test/functional/test_site/expected/testEmptyFrontmatter.html @@ -1,37 +1,40 @@ + - - - - - - Hello World - - - - - - - - - - - - + + + + + + Hello World + + + + + + + + + + + + + -
-
-

A page with an empty frontmatter should still build.

-
-
-
-
- Default footer +
+
+
+

A page with an empty frontmatter should still build.

+
+
+
+
+ Default footer +
+
-
-
@@ -39,11 +42,14 @@ - + - + + \ No newline at end of file diff --git a/test/functional/test_site/expected/testExternalScripts.html b/test/functional/test_site/expected/testExternalScripts.html index aa84ba4eb6..cedc9f6bbf 100644 --- a/test/functional/test_site/expected/testExternalScripts.html +++ b/test/functional/test_site/expected/testExternalScripts.html @@ -1,40 +1,43 @@ + - - - - - - Hello World - - - - - - - - - - - - + + + + + + Hello World + + + + + + + + + + + + + -
-
-

The external script MathJax 2.75 should be included, and the following MathJax content should render:

- - When \(a \ne 0\), there are two solutions to \(ax^2 + bx + c = 0\) and they are $$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$ - -
-
-
-
- Default footer +
+
+
+

The external script MathJax 2.75 should be included, and the following MathJax content should render:

+ + When \(a \ne 0\), there are two solutions to \(ax^2 + bx + c = 0\) and they are $$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$ + +
+
+
+
+ Default footer +
+
-
-
@@ -42,12 +45,15 @@ - + - + + \ No newline at end of file diff --git a/test/functional/test_site/expected/testLayouts.html b/test/functional/test_site/expected/testLayouts.html index d941aa4728..c5ef974144 100644 --- a/test/functional/test_site/expected/testLayouts.html +++ b/test/functional/test_site/expected/testLayouts.html @@ -1,53 +1,47 @@ + - - - - - - Hello World - - - - - - - - - - - - + + + + + + Hello World + + + + + + + + + + + + + -
+
- - -
-
-

Uses a front matter layout

+ +
+

Uses a front matter layout

+
+
+
+ Layout footer +
+
-
-
-
-
- Layout footer -
-
-
@@ -55,11 +49,14 @@

Uses a front matter layout - + - + + \ No newline at end of file diff --git a/test/functional/test_site/expected/testLayoutsOverride.html b/test/functional/test_site/expected/testLayoutsOverride.html index 879882d71b..46faa710c5 100644 --- a/test/functional/test_site/expected/testLayoutsOverride.html +++ b/test/functional/test_site/expected/testLayoutsOverride.html @@ -1,53 +1,47 @@ + - - - - - - Hello World - - - - - - - - - - - - + + + + + + Hello World + + + + + + + + + + + + + -
+
- - -
-
-

Uses a site.json layout, overriding front matter

+ +
+

Uses a site.json layout, overriding front matter

+
+
+
+ Layout footer +
+
-
-
-
-
- Layout footer -
-
-
@@ -55,11 +49,14 @@

Uses a site.json layout - + - + + \ No newline at end of file diff --git a/test/functional/test_site/expected/test_md_fragment.html b/test/functional/test_site/expected/test_md_fragment.html index 0ea3117ddd..ca3d19684b 100644 --- a/test/functional/test_site/expected/test_md_fragment.html +++ b/test/functional/test_site/expected/test_md_fragment.html @@ -1,38 +1,41 @@ + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + -
-
-

Some heading

-

The quick brown fox jumps over the lazy dog.

-
-
-
-
- Default footer +
+
+
+

Some heading

+

The quick brown fox jumps over the lazy dog.

+
+
+
+
+ Default footer +
+
-
-
@@ -40,11 +43,14 @@

Some heading< - + - + + \ No newline at end of file diff --git a/test/unit/Site.test.js b/test/unit/Site.test.js index 8137ac7225..e0e6da1573 100644 --- a/test/unit/Site.test.js +++ b/test/unit/Site.test.js @@ -5,6 +5,7 @@ const Site = require('../../src/Site'); const { FOOTER_MD_DEFAULT, + HEADER_MD_DEFAULT, INDEX_MD_DEFAULT, PAGE_EJS, SITE_JSON_DEFAULT, @@ -47,6 +48,7 @@ test('Site Generate builds the correct amount of assets', async () => { 'inner/_markbind/layouts/default/footer.md': '', 'inner/_markbind/layouts/default/head.md': '', + 'inner/_markbind/layouts/default/header.md': '', 'inner/_markbind/layouts/default/navigation.md': '', 'inner/_markbind/layouts/default/scripts.js': '', 'inner/_markbind/layouts/default/styles.css': '', @@ -56,7 +58,7 @@ test('Site Generate builds the correct amount of assets', async () => { await site.generate(); const paths = Object.keys(fs.vol.toJSON()); const originalNumFiles = Object.keys(json).length; - const expectedNumBuilt = 19; + const expectedNumBuilt = 20; expect(paths.length).toEqual(originalNumFiles + expectedNumBuilt); // site @@ -89,6 +91,7 @@ test('Site Generate builds the correct amount of assets', async () => { // layouts expect(fs.existsSync(path.resolve('inner/_site/markbind/layouts/default/footer.md'))).toEqual(true); expect(fs.existsSync(path.resolve('inner/_site/markbind/layouts/default/head.md'))).toEqual(true); + expect(fs.existsSync(path.resolve('inner/_site/markbind/layouts/default/header.md'))).toEqual(true); expect(fs.existsSync(path.resolve('inner/_site/markbind/layouts/default/navigation.md'))).toEqual(true); expect(fs.existsSync(path.resolve('inner/_site/markbind/layouts/default/scripts.js'))).toEqual(true); expect(fs.existsSync(path.resolve('inner/_site/markbind/layouts/default/styles.css'))).toEqual(true); @@ -103,7 +106,7 @@ test('Site Init in existing directory generates correct assets', async () => { await Site.initSite(''); const paths = Object.keys(fs.vol.toJSON()); const originalNumFiles = Object.keys(json).length; - const expectedNumBuilt = 13; + const expectedNumBuilt = 15; expect(paths.length).toEqual(originalNumFiles + expectedNumBuilt); // _boilerplates @@ -115,6 +118,9 @@ test('Site Init in existing directory generates correct assets', async () => { // head folder expect(fs.existsSync(path.resolve('_markbind/head'), 'utf8')).toEqual(true); + // header.md + expect(fs.readFileSync(path.resolve('_markbind/headers/header.md'), 'utf8')).toEqual(HEADER_MD_DEFAULT); + // site-nav.md expect(fs.readFileSync(path.resolve('_markbind/navigation/site-nav.md'), 'utf8')) .toEqual(SITE_NAV_MD_DEFAULT); @@ -147,7 +153,7 @@ test('Site Init in directory which does not exist generates correct assets', asy await Site.initSite('newDir'); const paths = Object.keys(fs.vol.toJSON()); const originalNumFiles = Object.keys(json).length; - const expectedNumBuilt = 13; + const expectedNumBuilt = 15; expect(paths.length).toEqual(originalNumFiles + expectedNumBuilt); @@ -160,6 +166,10 @@ test('Site Init in directory which does not exist generates correct assets', asy // head folder expect(fs.existsSync(path.resolve('newDir/_markbind/head'), 'utf8')).toEqual(true); + // header.md + expect(fs.readFileSync(path.resolve('newDir/_markbind/headers/header.md'), 'utf8')) + .toEqual(HEADER_MD_DEFAULT); + // site-nav.md expect(fs.readFileSync(path.resolve('newDir/_markbind/navigation/site-nav.md'), 'utf8')) .toEqual(SITE_NAV_MD_DEFAULT); diff --git a/test/unit/utils/data.js b/test/unit/utils/data.js index cd7558d240..0948101d70 100644 --- a/test/unit/utils/data.js +++ b/test/unit/utils/data.js @@ -1,6 +1,7 @@ module.exports.LAYOUT_FILES_DEFAULT = [ 'footer.md', 'head.md', + 'header.md', 'navigation.md', 'styles.css', ]; @@ -85,9 +86,19 @@ module.exports.FOOTER_MD_DEFAULT = '
\n' + '

\n' + '\n'; +module.exports.HEADER_MD_DEFAULT = '
\n' + + '
\n' + + '
\n' + + ' Start authoring your MarkBind website.\n' + + '
\n' + + '
\n' + + '
\n' + + '
\n'; + module.exports.INDEX_MD_DEFAULT = '\n' + ' title: "Hello World"\n' + ' footer: footer.md\n' + + ' header: header.md\n' + ' siteNav: site-nav.md\n' + '\n\n' + '# Hello world\n'