const CLASS_NAME_OVERFLOW = 'nav-overflow'
const CLASS_NAME_OVERFLOW_MENU = 'nav-overflow-menu'
+const CLASS_NAME_OVERFLOW_ITEM = 'nav-overflow-item'
const CLASS_NAME_HIDDEN = 'd-none'
const SELECTOR_NAV_ITEM = '.nav-item'
// Add overflow class to nav
this._element.classList.add(CLASS_NAME_OVERFLOW)
- // Get all nav items
- this._items = [...SelectorEngine.find(SELECTOR_NAV_ITEM, this._element)]
+ // Get all supported nav items from direct children:
+ // - .nav-item containers
+ // - direct .nav-link elements (e.g. button-based tabs)
+ this._items = [...this._element.children].filter(item => {
+ if (item.classList.contains(CLASS_NAME_OVERFLOW_ITEM) || item.classList.contains(CLASS_NAME_OVERFLOW_MENU)) {
+ return false
+ }
+
+ return item.matches(SELECTOR_NAV_ITEM) || item.matches(`${SELECTOR_NAV_LINK}:not(${SELECTOR_OVERFLOW_TOGGLE})`)
+ })
// Store original order data
for (const [index, item] of this._items.entries()) {
for (const item of items) {
// Clone the nav link as a dropdown item
- const link = SelectorEngine.findOne(SELECTOR_NAV_LINK, item)
+ const link = item.matches(SELECTOR_NAV_LINK) ? item : SelectorEngine.findOne(SELECTOR_NAV_LINK, item)
if (!link) {
continue
}
navOverflow.dispose()
})
+ it('should overflow direct button nav links', () => {
+ fixtureEl.innerHTML = [
+ '<div style="width: 250px;">',
+ ' <div class="nav nav-tabs" style="display: flex; width: 250px;" data-bs-toggle="nav-overflow">',
+ ' <button class="nav-link active" type="button" style="flex: 0 0 100px; width: 100px;">Tab 1</button>',
+ ' <button class="nav-link" type="button" style="flex: 0 0 100px; width: 100px;">Tab 2</button>',
+ ' <button class="nav-link" type="button" style="flex: 0 0 100px; width: 100px;">Tab 3</button>',
+ ' <button class="nav-link" type="button" style="flex: 0 0 100px; width: 100px;">Tab 4</button>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const navEl = fixtureEl.querySelector('[data-bs-toggle="nav-overflow"]')
+ const navOverflow = new NavOverflow(navEl)
+ const hiddenItems = navEl.querySelectorAll('.nav-link[data-bs-nav-overflow="true"]')
+ const dropdownButtons = navEl.querySelectorAll('.nav-overflow-menu button.dropdown-item')
+
+ expect(hiddenItems.length).toBeGreaterThan(0)
+ expect(dropdownButtons.length).toEqual(hiddenItems.length)
+
+ navOverflow.dispose()
+ })
+
it('should show overflow toggle when items overflow', () => {
fixtureEl.innerHTML = [
'<ul class="nav" style="display: flex; width: 250px;" data-bs-toggle="nav-overflow">',
- **Responds to container size**, not viewport size. The component uses a ResizeObserver to monitor its own width, so it works perfectly in embedded contexts, documentation examples, and responsive containers.
- Overflow items are cloned into a "More" dropdown menu while the originals are hidden.
- Works with all nav styles: default, pills, tabs, and underline.
+- Supports both `.nav-item > .nav-link` markup and direct `.nav-link` children (including button-based tabs).
- Active and disabled states are preserved in the overflow menu.
<Callout name="info-prefersreducedmotion" />