[JENKINS-75637] Fix System configuration page no longer having breadcrumb navigation (#10715)
Changelog Drafter / update_draft_release (push) Waiting to run Details
Changelog Drafter / jenkins_io_draft (push) Waiting to run Details
Label conflicting PRs / main (push) Waiting to run Details

This commit is contained in:
Kris Stern 2025-07-18 21:16:45 +08:00 committed by GitHub
commit a7652265f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 28 additions and 42 deletions

View File

@ -29,7 +29,7 @@ THE SOFTWARE.
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form"> <j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<l:layout permissions="${app.MANAGE_AND_SYSTEM_READ}" title="${%System}" type="one-column"> <l:layout permissions="${app.MANAGE_AND_SYSTEM_READ}" title="${%System}" type="one-column">
<st:include page="sidepanel.jelly" /> <st:include page="sidepanel.jelly" />
<l:breadcrumb title="${%System}" /> <f:breadcrumb-config-outline title="${%System}" />
<l:main-panel> <l:main-panel>
<j:set var="readOnlyMode" value="${!h.hasPermission(app.MANAGE)}"/> <j:set var="readOnlyMode" value="${!h.hasPermission(app.MANAGE)}"/>

View File

@ -32,5 +32,5 @@ THE SOFTWARE.
</st:attribute> </st:attribute>
</st:documentation> </st:documentation>
<l:breadcrumb title="${attrs.title?:'%configuration'}" id="inpage-nav" /> <l:breadcrumb title="${attrs.title?:'%configuration'}" id="inpage-nav" hasMenu="true" />
</j:jelly> </j:jelly>

View File

@ -48,10 +48,10 @@ THE SOFTWARE.
<j:if test="${mode=='breadcrumbs'}"> <j:if test="${mode=='breadcrumbs'}">
<j:set var="hasLink" value="${attrs.href != null}" /> <j:set var="hasLink" value="${attrs.href != null}" />
<li id="${attrs.id}" class="jenkins-breadcrumbs__list-item" data-type="breadcrumb-item" aria-current="${hasLink ? null : 'page'}"> <li id="${attrs.id}" class="jenkins-breadcrumbs__list-item" data-type="breadcrumb-item" aria-current="${hasLink ? null : 'page'}" data-has-menu="${attrs.hasMenu}">
<j:choose> <j:choose>
<j:when test="${!hasLink}"> <j:when test="${!hasLink}">
<span>${attrs.title}</span> <span class="${attrs.hasMenu ? 'hoverable-model-link' : ''}">${attrs.title}</span>
</j:when> </j:when>
<j:otherwise> <j:otherwise>
<a href="${attrs.href}" class="${attrs.hasMenu ? 'hoverable-model-link' : ''} ${attrs.hasChildrenMenu ? 'hoverable-children-model-link' : ''}"> <a href="${attrs.href}" class="${attrs.hasMenu ? 'hoverable-model-link' : ''} ${attrs.hasChildrenMenu ? 'hoverable-children-model-link' : ''}">

View File

@ -93,7 +93,6 @@ function init() {
0, 0,
function (e) { function (e) {
e.setAttribute("autocomplete", "off"); e.setAttribute("autocomplete", "off");
e.dataset["hideOnClick"] = "false";
// form field with auto-completion support // form field with auto-completion support
e.style.position = "relative"; e.style.position = "relative";
// otherwise menu won't hide on tab with nothing selected // otherwise menu won't hide on tab with nothing selected

View File

@ -5,12 +5,10 @@ import { toId } from "@/util/dom";
* sections on the page (if using <f:breadcrumb-config-outline />) * sections on the page (if using <f:breadcrumb-config-outline />)
*/ */
function init() { function init() {
const inpageNavigationBreadcrumb = document.querySelector("#inpage-nav"); const inpageNavigationBreadcrumb = document.querySelector("#inpage-nav span");
if (inpageNavigationBreadcrumb) { if (inpageNavigationBreadcrumb) {
const chevron = document.createElement("li"); inpageNavigationBreadcrumb.items = Array.from(
chevron.classList.add("children");
chevron.items = Array.from(
document.querySelectorAll( document.querySelectorAll(
"form > div > .jenkins-section > .jenkins-section__title", "form > div > .jenkins-section > .jenkins-section__title",
), ),
@ -18,8 +16,6 @@ function init() {
section.id = toId(section.textContent); section.id = toId(section.textContent);
return { label: section.textContent, url: "#" + section.id }; return { label: section.textContent, url: "#" + section.id };
}); });
inpageNavigationBreadcrumb.after(chevron);
} }
} }

View File

@ -38,13 +38,13 @@ function generateDropdowns() {
Utils.generateDropdown( Utils.generateDropdown(
element, element,
(instance) => { (instance) => {
const href = element.href;
if (element.items) { if (element.items) {
instance.setContent(Utils.generateDropdownItems(element.items)); instance.setContent(Utils.generateDropdownItems(element.items));
return; return;
} }
const href = element.href;
const hasModelLink = element.classList.contains( const hasModelLink = element.classList.contains(
"hoverable-model-link", "hoverable-model-link",
); );
@ -105,7 +105,7 @@ function generateDropdowns() {
instance.loaded = true; instance.loaded = true;
}); });
}, },
false, element.items != null,
{ {
trigger: "mouseenter", trigger: "mouseenter",
offset: [-16, 10], offset: [-16, 10],

View File

@ -22,8 +22,6 @@ function generateDropdown(element, callback, immediate, options = {}) {
{}, {},
Templates.dropdown(), Templates.dropdown(),
{ {
hideOnClick:
element.dataset["hideOnClick"] !== "false" ? "toggle" : false,
onCreate(instance) { onCreate(instance) {
const onload = () => { const onload = () => {
if (instance.loaded) { if (instance.loaded) {
@ -31,34 +29,18 @@ function generateDropdown(element, callback, immediate, options = {}) {
} }
document.addEventListener("click", (event) => { document.addEventListener("click", (event) => {
const isClickInAnyDropdown =
!!event.target.closest("[data-tippy-root]");
const isClickOnReference = instance.reference.contains( const isClickOnReference = instance.reference.contains(
event.target, event.target,
); );
// Don't close the dropdown if the user is interacting with a SELECT menu inside of it
const isSelect = event.target.tagName === "SELECT";
if (!isClickInAnyDropdown && !isClickOnReference) { if (!isClickOnReference && !isSelect) {
instance.clickToHide = true;
instance.hide(); instance.hide();
} }
}); });
instance.popper.addEventListener("mouseenter", () => {
const handleMouseMove = () => {
const dropdowns =
document.querySelectorAll("[data-tippy-root]");
const isMouseOverAnyDropdown = Array.from(dropdowns).some(
(dropdown) => dropdown.matches(":hover"),
);
if (!isMouseOverAnyDropdown) {
instance.hide();
document.removeEventListener("mousemove", handleMouseMove);
}
};
document.addEventListener("mousemove", handleMouseMove);
});
callback(instance); callback(instance);
}; };
if (immediate) { if (immediate) {
@ -69,13 +51,21 @@ function generateDropdown(element, callback, immediate, options = {}) {
}); });
} }
}, },
onHide() { onHide(instance) {
const dropdowns = document.querySelectorAll("[data-tippy-root]"); if (
const isMouseOverAnyDropdown = Array.from(dropdowns).some( instance.props.trigger === "mouseenter" &&
(dropdown) => dropdown.matches(":hover"), !instance.clickToHide
); ) {
const dropdowns = document.querySelectorAll("[data-tippy-root]");
const isMouseOverAnyDropdown = Array.from(dropdowns).some(
(dropdown) => dropdown.matches(":hover"),
);
return !isMouseOverAnyDropdown; return !isMouseOverAnyDropdown;
}
instance.clickToHide = false;
return true;
}, },
}, },
options, options,

View File

@ -3,6 +3,7 @@ html {
box-sizing: border-box; box-sizing: border-box;
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
color: var(--text-color); color: var(--text-color);
scroll-padding-top: calc(var(--header-height) + var(--section-padding));
} }
body { body {