diff --git a/packages/vue-components/src/Dropdown.vue b/packages/vue-components/src/Dropdown.vue index e5297ac3db..a85a43c4f9 100644 --- a/packages/vue-components/src/Dropdown.vue +++ b/packages/vue-components/src/Dropdown.vue @@ -56,6 +56,7 @@ import Submenu from './Submenu.vue'; import { toBoolean } from './utils/utils'; import $ from './utils/NodeList'; +import preventOverflowOnMobile from './utils/dropdown'; export default { components: { @@ -84,6 +85,9 @@ export default { hasParentDropdown: { default: undefined, }, + isParentNavbar: { + default: false, + }, }, data() { return { @@ -131,11 +135,24 @@ export default { }, hideDropdownMenu() { this.show = false; - $(this.$refs.dropdown).findChildren('ul').each(ul => ul.classList.toggle('show', false)); + $(this.$refs.dropdown).findChildren('ul').each((ul) => { + ul.classList.toggle('show', false); + + if (window.innerWidth < 768 && this.isParentNavbar) { + ul.style.removeProperty('left'); + } + }); }, showDropdownMenu() { this.show = true; - $(this.$refs.dropdown).findChildren('ul').each(ul => ul.classList.toggle('show', true)); + $(this.$refs.dropdown).findChildren('ul').each((ul) => { + ul.classList.toggle('show', true); + + // check if the dropdown is part of the sliding menu on mobile + if (window.innerWidth < 768 && this.isParentNavbar) { + preventOverflowOnMobile(ul); + } + }); }, }, mounted() { @@ -169,6 +186,24 @@ export default { diff --git a/packages/vue-components/src/Submenu.vue b/packages/vue-components/src/Submenu.vue index f573eedca4..c35e93391b 100644 --- a/packages/vue-components/src/Submenu.vue +++ b/packages/vue-components/src/Submenu.vue @@ -25,6 +25,7 @@ import { toBoolean } from './utils/utils'; import $ from './utils/NodeList'; import positionSubmenu from './utils/submenu'; +import preventOverflowOnMobile from './utils/dropdown'; export default { props: { @@ -44,6 +45,11 @@ export default { dropleft: false, }; }, + inject: { + isParentNavbar: { + default: false, + }, + }, computed: { disabledBool() { return toBoolean(this.disabled); @@ -59,6 +65,13 @@ export default { this.show = true; $(this.$refs.submenu).findChildren('ul').each((ul) => { ul.classList.toggle('show', true); + + // check if submenu is part of the navbar sliding menu on mobile + if (window.innerWidth < 768 && this.isParentNavbar) { + preventOverflowOnMobile(ul); + return; + } + if (positionSubmenu.isRightAlign(ul)) { this.alignMenuRight(); } else { diff --git a/packages/vue-components/src/__tests__/__snapshots__/Navbar.spec.js.snap b/packages/vue-components/src/__tests__/__snapshots__/Navbar.spec.js.snap index 5fef17d709..0658a93736 100644 --- a/packages/vue-components/src/__tests__/__snapshots__/Navbar.spec.js.snap +++ b/packages/vue-components/src/__tests__/__snapshots__/Navbar.spec.js.snap @@ -9,7 +9,7 @@ exports[`Navbar and secondary navbar navbar with page nav button 1`] = ` class="container-fluid" > - - + + @@ -86,7 +74,7 @@ exports[`Navbar and secondary navbar navbar with site and page nav buttons 1`] = class="container-fluid" > - - + + @@ -165,7 +141,7 @@ exports[`Navbar and secondary navbar navbar with site nav button 1`] = ` class="container-fluid" > - - + + @@ -242,7 +206,7 @@ exports[`Navbar and secondary navbar navbar without site and page nav buttons 1` class="container-fluid" > - - + + diff --git a/packages/vue-components/src/utils/dropdown.js b/packages/vue-components/src/utils/dropdown.js new file mode 100644 index 0000000000..fb93264f25 --- /dev/null +++ b/packages/vue-components/src/utils/dropdown.js @@ -0,0 +1,23 @@ +export default function preventOverflowOnMobile(el) { + // get highest-level dropdown menu + let rootDropdownMenu = el; + let currentEl = el; + while (currentEl) { + if (currentEl.classList && currentEl.classList.contains('dropdown-menu')) { + rootDropdownMenu = currentEl; + } + currentEl = currentEl.parentNode; + } + + // shift dropdown relative to its parent and prevent overflow if necessary + if (rootDropdownMenu.offsetWidth > window.innerWidth) { + rootDropdownMenu.setAttribute('style', 'left: 0px;'); + } else { + const dropdownPosition = rootDropdownMenu.parentNode.getBoundingClientRect(); + const overflowedWidth = dropdownPosition.left + rootDropdownMenu.offsetWidth - window.innerWidth; + const leftPosition = overflowedWidth < 0 + ? dropdownPosition.left + : dropdownPosition.left - overflowedWidth; + rootDropdownMenu.setAttribute('style', `left: ${leftPosition}px;`); + } +}