From fb628c924fc7e9563320a79bdba04e443aeb39b5 Mon Sep 17 00:00:00 2001 From: jonahtanjz Date: Tue, 2 Feb 2021 22:19:54 +0800 Subject: [PATCH] Update site and page navigation --- docs/userGuide/syntax/navBars.mbdf | 23 ++ .../userGuide/syntax/pageNavigationMenus.mbdf | 2 + .../userGuide/syntax/siteNavigationMenus.mbdf | 1 + packages/core-web/src/index.js | 12 +- packages/core-web/src/styles/nav-menu-bar.css | 60 +-- packages/core/src/Page/index.js | 20 +- packages/core/src/html/siteNavProcessor.js | 2 +- packages/vue-components/src/Navbar.vue | 101 ++++-- packages/vue-components/src/PageNavButton.vue | 98 +++++ packages/vue-components/src/SiteNavButton.vue | 88 +++++ .../src/__tests__/Navbar.spec.js | 87 +++++ .../__snapshots__/Navbar.spec.js.snap | 343 ++++++++++++++++++ packages/vue-components/src/index.js | 4 + packages/vue-components/src/utils/utils.js | 39 ++ 14 files changed, 783 insertions(+), 97 deletions(-) create mode 100644 packages/vue-components/src/PageNavButton.vue create mode 100644 packages/vue-components/src/SiteNavButton.vue create mode 100644 packages/vue-components/src/__tests__/Navbar.spec.js create mode 100644 packages/vue-components/src/__tests__/__snapshots__/Navbar.spec.js.snap diff --git a/docs/userGuide/syntax/navBars.mbdf b/docs/userGuide/syntax/navBars.mbdf index 5707b0c9fb..adfb8c0538 100644 --- a/docs/userGuide/syntax/navBars.mbdf +++ b/docs/userGuide/syntax/navBars.mbdf @@ -158,3 +158,26 @@ Name | Description + +****Page and site navigation menus**** +Both [site navigation]({{ baseUrl }}/userGuide/usingComponents.html#site-navigation-menus) and [page navigation]({{ baseUrl }}/userGuide/usingComponents.html#page-navigation-menus) menus will be hidden on smaller screens. +To make these accessible on smaller screens, you can use the `` and `` components in the `lower-navbar` slot. By default, if the `lower-navbar` slot is not specified, both the site and +page navigation buttons will automatically be added if they exist. + +```html + + + MarkBind +
  • Highlighted Link
  • + + +
    +``` + +Component | Description +--- | --- +`page-nav-button` | Will pull everything wrapped with an identifier, `id=page-nav`, in the layout file into the menu. Alternatively, if the identifier is not present, it will pull the page navigation menu in automatically if it exist. +`site-nav-button` | Will pull everything wrapped with an identifier, `id=site-nav`, in the layout file into the menu. Alternatively, if the identifier is not present, it will pull the first site navigation menu in automatically if it exist. \ No newline at end of file diff --git a/docs/userGuide/syntax/pageNavigationMenus.mbdf b/docs/userGuide/syntax/pageNavigationMenus.mbdf index a650164193..935460605f 100644 --- a/docs/userGuide/syntax/pageNavigationMenus.mbdf +++ b/docs/userGuide/syntax/pageNavigationMenus.mbdf @@ -17,6 +17,8 @@ 3. **Position the page navigation menu** within your layout using the `{% raw %}{{ pageNav }}{% endraw %}` variable. +4. **(Optional) To make pageNav accessible on smaller screens, you can use the `` component in the [navbar]({{baseUrl}}/userGuide/usingComponents.html#navbars).** +
    {{ icon_example }} diff --git a/docs/userGuide/syntax/siteNavigationMenus.mbdf b/docs/userGuide/syntax/siteNavigationMenus.mbdf index c5c501cf3e..9bb93ddb3c 100644 --- a/docs/userGuide/syntax/siteNavigationMenus.mbdf +++ b/docs/userGuide/syntax/siteNavigationMenus.mbdf @@ -7,6 +7,7 @@ Steps to add a siteNav: 1. Format your siteNav as an unordered Markdown list 2. Include it under a `` element. +3. (Optional) To make siteNav accessible on smaller screens, you can use the `` component in the [navbar]({{baseUrl}}/userGuide/usingComponents.html#navbars).
    diff --git a/packages/core-web/src/index.js b/packages/core-web/src/index.js index 6be4a04cef..6ca1b68a80 100644 --- a/packages/core-web/src/index.js +++ b/packages/core-web/src/index.js @@ -1,7 +1,6 @@ // eslint-disable-next-line import/no-extraneous-dependencies import MarkBindVue from '@markbind/vue-components/src'; import initScrollTopButton from './scrollTopButton'; -import initNavMenuBar from './navMenuBar'; import './styles/index.css'; Vue.use(MarkBindVue); @@ -38,15 +37,7 @@ function detectAndApplyFixedHeaderStyles() { const headerHeight = headerSelector.height(); const bufferHeight = 1; - const navMenuBarHeight = 50; - insertCss(`.fixed-header-padding { - padding-top: ${headerHeight}px !important; - } - @media (max-width: 992px) { - .fixed-header-padding { - padding-top: ${headerHeight + navMenuBarHeight}px !important; - } - }`); + insertCss(`.fixed-header-padding { padding-top: ${headerHeight}px !important }`); insertCss( `span.anchor { position: relative; @@ -160,6 +151,5 @@ window.popoverInnerGetters = { window.tooltipInnerContentGetter = makeMbSlotGetter('_content'); initScrollTopButton(); -initNavMenuBar(); export default { setup, setupWithSearch }; diff --git a/packages/core-web/src/styles/nav-menu-bar.css b/packages/core-web/src/styles/nav-menu-bar.css index dd67689b18..c8aa97f31b 100644 --- a/packages/core-web/src/styles/nav-menu-bar.css +++ b/packages/core-web/src/styles/nav-menu-bar.css @@ -1,65 +1,39 @@ -@media (min-width: 993px) { - #nav-menu-bar { - display: none !important; - } -} - -@media (max-width: 575px) { - #nav-menu-bar { - margin: 0px -10px; - } -} - -@media (min-width: 576px) and (max-width: 992px) { - #nav-menu-bar { - margin: 0px -20px; - } +.hide-nav-button { + display: none; } -#nav-menu-bar { - position: fixed; - top: 66px; - width: 100%; - z-index: 1000; - border-bottom: 1px solid #C1C1C1; +.nav-menu-container { + background-color: #fff; + border-bottom: 1px solid #c1c1c1; height: 50px; - background: #F8F8F8; - display: none; + position: relative; } -#toggle-site-nav-button { - float: left; - padding: 15px; - display: none; -} - -#toggle-site-nav-button:before { - content: "\e236"; - font-size: 20px; +.nav-menu-container > div > #toggle-site-nav-button { + position: absolute; + left: 0; } -#toggle-page-nav-button { - float: right; - padding: 15px; - display: none; +.nav-menu-container > div > #toggle-page-nav-button { + position: absolute; + right: 0; } -#toggle-page-nav-button:before { - content:"\e235"; - font-size: 20px; +.nav-menu-container > div > .site-nav-menu, +.nav-menu-container > div > .page-nav-menu { + padding-top: 50px; } -.nav-menu-close-icon:before { +.nav-menu-close-icon::before { content: "\e014" !important; } .nav-menu-open { display: block !important; - position: fixed !important; width: 100% !important; max-width: 100% !important; background: #fff; - top: 50px !important; height: 100%; max-height: 100% !important; + clear: both; } diff --git a/packages/core/src/Page/index.js b/packages/core/src/Page/index.js index 0905d9236d..a9db385ffd 100644 --- a/packages/core/src/Page/index.js +++ b/packages/core/src/Page/index.js @@ -347,7 +347,7 @@ class Page { const headingStack = []; Object.keys(this.navigableHeadings).forEach((key) => { const currentHeadingLevel = this.navigableHeadings[key].level; - const currentHeadingHTML = `` + const currentHeadingHTML = `` + `${this.navigableHeadings[key].text}‎\n`; const nestedHeadingHTML = '\n'; } return ''; @@ -455,7 +454,6 @@ class Page { let content = variableProcessor.renderWithSiteVariables(this.pageConfig.sourcePath, pageSources); content = await nodeProcessor.process(this.pageConfig.sourcePath, content); this.processFrontMatter(nodeProcessor.frontMatter); - content = Page.addNavMenuBar(content); content = Page.addScrollToTopButton(content); content = pluginManager.postRender(this.frontMatter, content); const pageContent = content; @@ -463,7 +461,7 @@ class Page { pluginManager.collectPluginPageNjkAssets(this.frontMatter, content, this.asset); await layoutManager.generateLayoutIfNeeded(this.layout); - const pageNav = Page.addNavMenuScript(this.buildPageNav(content)); + const pageNav = this.buildPageNav(content); content = layoutManager.combineLayoutWithPage(this.layout, content, pageNav, this.includedFiles); this.asset = { ...this.asset, @@ -484,18 +482,6 @@ class Page { this.collectHeadingsAndKeywords(pageContent); } - static addNavMenuBar(pageData) { - const menuBar = ''; - return `${pageData}\n${menuBar}`; - } - - static addNavMenuScript(pageData) { - const script = ''; - return `${pageData}\n${script}`; - } - static addScrollToTopButton(pageData) { const button = ''; diff --git a/packages/core/src/html/siteNavProcessor.js b/packages/core/src/html/siteNavProcessor.js index d165e9efad..97861da5a6 100644 --- a/packages/core/src/html/siteNavProcessor.js +++ b/packages/core/src/html/siteNavProcessor.js @@ -67,7 +67,7 @@ function renderSiteNav(node) { }); $original.empty(); - $original.append($('site-nav').children()).append(''); + $original.append($('site-nav').children()); } module.exports = { diff --git a/packages/vue-components/src/Navbar.vue b/packages/vue-components/src/Navbar.vue index 874b5cffa1..d068cf973e 100644 --- a/packages/vue-components/src/Navbar.vue +++ b/packages/vue-components/src/Navbar.vue @@ -1,36 +1,46 @@ diff --git a/packages/vue-components/src/PageNavButton.vue b/packages/vue-components/src/PageNavButton.vue new file mode 100644 index 0000000000..90e1aebe95 --- /dev/null +++ b/packages/vue-components/src/PageNavButton.vue @@ -0,0 +1,98 @@ + + + + + diff --git a/packages/vue-components/src/SiteNavButton.vue b/packages/vue-components/src/SiteNavButton.vue new file mode 100644 index 0000000000..81671279b0 --- /dev/null +++ b/packages/vue-components/src/SiteNavButton.vue @@ -0,0 +1,88 @@ + + + + + diff --git a/packages/vue-components/src/__tests__/Navbar.spec.js b/packages/vue-components/src/__tests__/Navbar.spec.js new file mode 100644 index 0000000000..1486a7e2e3 --- /dev/null +++ b/packages/vue-components/src/__tests__/Navbar.spec.js @@ -0,0 +1,87 @@ +import { mount } from '@vue/test-utils'; +import Navbar from '../Navbar.vue'; +import PageNavButton from '../PageNavButton.vue'; +import SiteNavButton from '../SiteNavButton.vue'; + +const DEFAULT_STUBS = { + 'page-nav-button': PageNavButton, + 'site-nav-button': SiteNavButton, +}; + +const NAVBAR_CONTENT = ` +Your Logo +
  • Topic 1
  • +
  • Topic 2
  • + +`; + +const OMIT_PAGE_AND_SITE_NAV_BUTTONS = '
    '; + +const SITE_NAV_BUTTON = ` + +`; + +const PAGE_NAV_BUTTON = ` + +`; + +const SITE_AND_PAGE_NAV_BUTTONS = ` + +`; + +describe('Navbar and secondary navbar', () => { + test('navbar without site and page nav buttons', async () => { + const wrapper = mount(Navbar, { + slots: { + default: NAVBAR_CONTENT, + 'lower-navbar': OMIT_PAGE_AND_SITE_NAV_BUTTONS, + }, + stubs: DEFAULT_STUBS, + }); + + expect(wrapper.element).toMatchSnapshot(); + }); + + test('navbar with site nav button', async () => { + const wrapper = mount(Navbar, { + slots: { + default: NAVBAR_CONTENT, + 'lower-navbar': SITE_NAV_BUTTON, + }, + stubs: DEFAULT_STUBS, + }); + + expect(wrapper.element).toMatchSnapshot(); + }); + + test('navbar with page nav button', async () => { + const wrapper = mount(Navbar, { + slots: { + default: NAVBAR_CONTENT, + 'lower-navbar': PAGE_NAV_BUTTON, + }, + stubs: DEFAULT_STUBS, + }); + + expect(wrapper.element).toMatchSnapshot(); + }); + + test('navbar with site and page nav buttons', async () => { + const wrapper = mount(Navbar, { + slots: { + default: NAVBAR_CONTENT, + 'lower-navbar': SITE_AND_PAGE_NAV_BUTTONS, + }, + stubs: DEFAULT_STUBS, + }); + + expect(wrapper.element).toMatchSnapshot(); + }); +}); diff --git a/packages/vue-components/src/__tests__/__snapshots__/Navbar.spec.js.snap b/packages/vue-components/src/__tests__/__snapshots__/Navbar.spec.js.snap new file mode 100644 index 0000000000..62c48c84d2 --- /dev/null +++ b/packages/vue-components/src/__tests__/__snapshots__/Navbar.spec.js.snap @@ -0,0 +1,343 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Navbar and secondary navbar navbar with page nav button 1`] = ` +
    + + +
    + +
    +`; + +exports[`Navbar and secondary navbar navbar with site and page nav buttons 1`] = ` +
    + + +
    + +`; + +exports[`Navbar and secondary navbar navbar with site nav button 1`] = ` +
    + + +
    + +
    +`; + +exports[`Navbar and secondary navbar navbar without site and page nav buttons 1`] = ` +
    + + +
    +
    +
    +
    +`; diff --git a/packages/vue-components/src/index.js b/packages/vue-components/src/index.js index 72493e927d..93ab5afec1 100644 --- a/packages/vue-components/src/index.js +++ b/packages/vue-components/src/index.js @@ -26,6 +26,8 @@ import tipBox from './TipBox.vue'; import trigger from './Trigger.vue'; import siteNav from './SiteNav.vue'; import submenu from './Submenu.vue'; +import siteNavButton from './SiteNavButton.vue'; +import pageNavButton from './PageNavButton.vue'; const components = { box: tipBox, @@ -46,6 +48,8 @@ const components = { trigger, siteNav, submenu, + siteNavButton, + pageNavButton, }; const directives = { diff --git a/packages/vue-components/src/utils/utils.js b/packages/vue-components/src/utils/utils.js index 7cabe3e9be..98414a2ecf 100644 --- a/packages/vue-components/src/utils/utils.js +++ b/packages/vue-components/src/utils/utils.js @@ -1,3 +1,5 @@ +import { BIconFileEarmarkSpreadsheet } from "bootstrap-vue"; + // coerce convert som types of data into another type export const coerce = { // Convert a string to booleam. Otherwise, return the value without modification, so if is not boolean, Vue throw a warning. @@ -159,3 +161,40 @@ export function VueFixer (vue) { vue.mixins.unshift(mixin) return vue } + +export function resizeHeader(isNavMenuShowing) { + const elements = ['.fixed-header-padding', 'span.anchor', 'span.card-container::before']; + const headerHeight = document.querySelector("header[fixed]").offsetHeight; + for (const element of elements) { + const selectedElements = document.querySelectorAll(element); + for (const selectedElement of selectedElements) { + if (isNavMenuShowing) { + switch (element) { + case elements[0]: + selectedElement.style.setProperty('padding-top', `${headerHeight}px`, 'important'); + break; + case elements[1]: + selectedElement.style.setProperty('top', `calc(-${headerHeight}px - 1rem)`); + break; + case elements[2]: + selectedElement.style.setProperty('margin-top', `calc(-${headerHeight}px - 1rem)`); + selectedElement.style.setProperty('height', `calc(${headerHeight}px + 1rem)`); + break; + } + } else { + switch (element) { + case elements[0]: + selectedElement.style.removeProperty('padding-top'); + break; + case elements[1]: + selectedElement.style.removeProperty('top'); + break; + case elements[2]: + selectedElement.style.removeProperty('margin-top'); + selectedElement.style.removeProperty('height'); + break; + } + } + } + } +}