Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/assets/stylesheets/application.css.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
@import "partials/_record";
@import "partials/_search";
@import "partials/_shared";
@import "partials/_source_tabs";
@import "partials/_results";
@import "partials/_loading_spinner";
@import "partials/_suggestion-panel";
46 changes: 0 additions & 46 deletions app/assets/stylesheets/partials/_search.scss
Original file line number Diff line number Diff line change
Expand Up @@ -214,52 +214,6 @@
color: #fff;
}

/* =========== */
/* New Tab Bar */
/* =========== */

#tabs {
margin-top: 0;

ul {
list-style-type: none;
padding: 0;
margin: 0;
display: flex;
gap: 4px;
}

a {
padding: 12px 20px 16px;
background-color: transparent;
display: inline-block;
border: 2px solid transparent;
border-bottom: 0;

@include searchUnderlinedLinks;

&:hover {
border-color: $color-gray-700;
background-color: $color-gray-900;
color: $color-text-oncolor;
}

&.active {
background-color: $color-white;
color: $color-text-primary;
text-decoration: none;

&:hover {
color: $color-text-primary;
border-color: transparent;
}
}


}

}

/* ============== */
/* Search Actions */
/* ============== */
Expand Down
103 changes: 103 additions & 0 deletions app/assets/stylesheets/partials/_source_tabs.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/* ============================ */
/* Source Tab Bar in USE Search */
/* ============================ */

#tabs {
margin-top: 0;
position: relative;

// Graceful degradation when JS is not enabled
&:not(.has-js) {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}

// Display styles for the main tab bar
ul.primary {
list-style-type: none;
padding: 0;
margin: 0;
display: flex;
gap: 4px;
}

li {

&.--hidden {
display: none;
}

}

a, button {
padding: 12px 20px 16px;
background-color: transparent;
display: inline-block;
border: 2px solid transparent;
border-bottom: 0;
white-space: nowrap;

@include searchUnderlinedLinks;

&:hover {
border-color: $color-gray-700;
background-color: $color-gray-900;
color: $color-text-oncolor;
}

&.active {
background-color: $color-white;
color: $color-text-primary;
text-decoration: none;

&:hover {
color: $color-text-primary;
border-color: transparent;
}
}

}

// Swap the icon to chevron-up when menu is open
button[aria-expanded="true"] {

i::before {
content: '\f077';
}

}

// Display styles for the dropdown menu under the "More" button
ul.-secondary {
width: 100%;
display: none;
position: absolute;
top: 100%;
right: 0;
list-style-type: none;
padding: 0;
margin: 0;
z-index: 999;

li {
width: 100%;

a {
background-color: $color-gray-950;
width: 100%;

&:hover {
border-color: $color-gray-950;
background-color: $color-gray-900;
}
}

}

}

&.--show-secondary .-secondary {
display: block;
}

}
4 changes: 4 additions & 0 deletions app/javascript/loading_spinner.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ document.addEventListener('click', function(event) {

// Handle tab clicks
if (clickedElement.closest('.tab-navigation')) {

// If the element is NOT a link, don't show the spinner
if (clickedElement.nodeName !== "A") { return; }

const clickedParams = new URLSearchParams(clickedElement.search);
const newTab = clickedParams.get('tab');

Expand Down
89 changes: 89 additions & 0 deletions app/javascript/source_tabs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// ===========================================================================
Copy link
Member

@JPrevost JPrevost Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if the is an edge case you prefer to come back to, but if I start with a wide viewport from the USE UI home and then run a search and then narrow my viewport it does not collapse.

If I then refresh, I get the collapsed mode and it works when I expand and contract the viewport as expected.

This issue is being caused by the JS throwing an error and stopping execution.

// RESPONSIVE TAB BAR LOGIC WITH GRACEFUL DEGRADATION
// Source: https://css-tricks.com/container-adapting-tabs-with-more-button/
// ===========================================================================

// Store references to relevant selectors
const container = document.querySelector('#tabs')
const primary = container.querySelector('.primary')
Copy link
Member

@JPrevost JPrevost Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get this error in the console when this is loaded from the main USE UI page.

null is not an object (evaluating 'container.querySelector')

We likely either need to change this logic here or to not error which prevents the script from running further or only load this script on pages that have that selector.

This script erroring prevents it from working when a search is run.

One way to address this might be to wrap this logic in a function and then have a return block at the start of the function if the required DOM elements are not present.

const primaryItems = container.querySelectorAll('.primary > li:not(.-more)')

// Add a class to turn off graceful degradation style
container.classList.add('has-js')

// insert "more" button and duplicate the original tab bar items
primary.insertAdjacentHTML('beforeend', `
<li class="-more">
<button type="button" aria-haspopup="true" aria-expanded="false" aria-controls="more-options">
More <i class="fa-light fa-chevron-down"></i>
</button>
<ul class="-secondary" id="more-options" aria-label="More options">
${primary.innerHTML}
</ul>
</li>
`)
const secondary = container.querySelector('.-secondary')
const secondaryItems = secondary.querySelectorAll('li')
const allItems = container.querySelectorAll('li')
const moreLi = primary.querySelector('.-more')
const moreBtn = moreLi.querySelector('button')

// When the more button is clicked, toggle classes to indicate the secondary menu is open
moreBtn.addEventListener('click', (e) => {
e.preventDefault()
container.classList.toggle('--show-secondary')
moreBtn.setAttribute('aria-expanded', container.classList.contains('--show-secondary'))
})

// adapt tabs
const doAdapt = () => {

// reveal all items for the calculation
allItems.forEach((item) => {
item.classList.remove('--hidden')
})

// hide items that won't fit in the Primary tab bar
let stopWidth = moreBtn.offsetWidth
let hiddenItems = []
const primaryWidth = primary.offsetWidth
primaryItems.forEach((item, i) => {
if(primaryWidth >= stopWidth + item.offsetWidth) {
stopWidth += item.offsetWidth
} else {
item.classList.add('--hidden')
hiddenItems.push(i)
}
})

// toggle the visibility of More button and items in Secondary menu
if(!hiddenItems.length) {
moreLi.classList.add('--hidden')
container.classList.remove('--show-secondary')
moreBtn.setAttribute('aria-expanded', false)
}
else {
secondaryItems.forEach((item, i) => {
if(!hiddenItems.includes(i)) {
item.classList.add('--hidden')
}
})
}
}

// Adapt the tabs to fit the viewport
doAdapt() // immediately on load
window.addEventListener('resize', doAdapt) // on window resize

// hide Secondary menu on the outside click
document.addEventListener('click', (e) => {
let el = e.target
while(el) {
if(el === moreBtn) {
return;
}
el = el.parentNode
}
container.classList.remove('--show-secondary')
moreBtn.setAttribute('aria-expanded', false)
})
8 changes: 5 additions & 3 deletions app/views/search/_source_tabs.html.erb
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
<!-- Tab Navigation -->
<nav id="tabs" class="tab-navigation" aria-label="Result type navigation">
<ul>
<ul class="primary">
<li><%= link_to_tab("All") %></li>

<% if Feature.enabled?(:tab_primo_all) %>
<li><%= link_to_tab("Primo", "Articles and Catalog") %></li>
<% end %>

<li><%= link_to_tab("cdi", "Articles") %></li>
<li><%= link_to_tab("alma", "Catalog") %></li>
<li><%= link_to_tab("alma", "Books and media") %></li>

<% if Feature.enabled?(:tab_timdex_all) %>
<li><%= link_to_tab("TIMDEX") %></li>
<% end %>
<% if Feature.enabled?(:tab_timdex_alma) %>
<li><%= link_to_tab("timdex_alma", "Alma (TIMDEX)") %></li>
<% end %>
<li><%= link_to_tab("aspace", "MIT archives") %></li>
<li><%= link_to_tab("Website") %></li>
<li><%= link_to_tab("aspace", "Archival materials") %></li>
</ul>
</nav>

<%= javascript_include_tag "source_tabs" %>