diff --git a/core/src/main/java/hudson/model/ParametersAction.java b/core/src/main/java/hudson/model/ParametersAction.java index 9491510743..e262909e47 100644 --- a/core/src/main/java/hudson/model/ParametersAction.java +++ b/core/src/main/java/hudson/model/ParametersAction.java @@ -48,6 +48,7 @@ import java.util.TreeSet; import java.util.logging.Level; import java.util.logging.Logger; import jenkins.model.RunAction2; +import jenkins.model.experimentalflags.NewBuildPageUserExperimentalFlag; import jenkins.util.SystemProperties; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -208,6 +209,12 @@ public class ParametersAction implements RunAction2, Iterable, Q @Override public String getIconFileName() { + Boolean newBuildPageEnabled = new NewBuildPageUserExperimentalFlag().getFlagValue(); + + if (newBuildPageEnabled) { + return null; + } + return "symbol-parameters"; } diff --git a/core/src/main/java/jenkins/model/ParameterizedJobMixIn.java b/core/src/main/java/jenkins/model/ParameterizedJobMixIn.java index 98a12424bd..ea3b099d0c 100644 --- a/core/src/main/java/jenkins/model/ParameterizedJobMixIn.java +++ b/core/src/main/java/jenkins/model/ParameterizedJobMixIn.java @@ -29,6 +29,8 @@ import static jakarta.servlet.http.HttpServletResponse.SC_CONFLICT; import static jakarta.servlet.http.HttpServletResponse.SC_CREATED; import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.Extension; import hudson.Util; import hudson.cli.declarative.CLIMethod; import hudson.cli.declarative.CLIResolver; @@ -60,6 +62,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; +import jenkins.model.details.Detail; +import jenkins.model.details.DetailFactory; +import jenkins.model.details.ParameterizedDetail; import jenkins.model.lazy.LazyBuildMixIn; import jenkins.security.stapler.StaplerNotDispatchable; import jenkins.triggers.SCMTriggerItem; @@ -561,6 +566,25 @@ public abstract class ParameterizedJobMixIn & Param return !isDisabled() && !((Job) this).isHoldOffBuildUntilSave(); } + @Extension + final class ParameterizedDetailFactory extends DetailFactory { + + @Override + public Class type() { + return Run.class; + } + + @NonNull + @Override public List createFor(@NonNull Run target) { + var action = target.getAction(ParametersAction.class); + + if (action == null || action.getParameters().isEmpty()) { + return List.of(); + } + + return List.of(new ParameterizedDetail(target)); + } + } } } diff --git a/core/src/main/java/jenkins/model/details/ParameterizedDetail.java b/core/src/main/java/jenkins/model/details/ParameterizedDetail.java new file mode 100644 index 0000000000..173f9ba9b6 --- /dev/null +++ b/core/src/main/java/jenkins/model/details/ParameterizedDetail.java @@ -0,0 +1,27 @@ +package jenkins.model.details; + +import edu.umd.cs.findbugs.annotations.Nullable; +import hudson.model.ParametersAction; +import hudson.model.Run; + +/** + * Displays if a run has parameters + */ +public class ParameterizedDetail extends Detail { + + public final ParametersAction action; + + public ParameterizedDetail(Run run) { + super(run); + this.action = getObject().getAction(ParametersAction.class); + } + + public @Nullable String getIconClassName() { + return "symbol-parameters"; + } + + @Override + public @Nullable String getDisplayName() { + return action.getDisplayName(); + } +} diff --git a/core/src/main/resources/hudson/model/BooleanParameterValue/value.jelly b/core/src/main/resources/hudson/model/BooleanParameterValue/value.jelly index 3d249ff3ab..137dd7b134 100644 --- a/core/src/main/resources/hudson/model/BooleanParameterValue/value.jelly +++ b/core/src/main/resources/hudson/model/BooleanParameterValue/value.jelly @@ -27,8 +27,8 @@ THE SOFTWARE. xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:i="jelly:fmt" xmlns:p="/lib/hudson/project"> - - - - + +
+ +
diff --git a/core/src/main/resources/hudson/model/StringParameterValue/value.jelly b/core/src/main/resources/hudson/model/StringParameterValue/value.jelly index 7c5328f667..eeed9e762e 100644 --- a/core/src/main/resources/hudson/model/StringParameterValue/value.jelly +++ b/core/src/main/resources/hudson/model/StringParameterValue/value.jelly @@ -28,7 +28,9 @@ THE SOFTWARE. xmlns:i="jelly:fmt" xmlns:p="/lib/hudson/project"> - - +
+ ${it.value} + +
\ No newline at end of file diff --git a/core/src/main/resources/jenkins/model/details/ParameterizedDetail/detail.jelly b/core/src/main/resources/jenkins/model/details/ParameterizedDetail/detail.jelly new file mode 100644 index 0000000000..aacf793548 --- /dev/null +++ b/core/src/main/resources/jenkins/model/details/ParameterizedDetail/detail.jelly @@ -0,0 +1,52 @@ + + + + + +
+ + + + + + + + + + +
+
+ + +
diff --git a/core/src/main/resources/lib/layout/dialog.jelly b/core/src/main/resources/lib/layout/dialog.jelly new file mode 100644 index 0000000000..b1656e9ac1 --- /dev/null +++ b/core/src/main/resources/lib/layout/dialog.jelly @@ -0,0 +1,44 @@ + + + + + + The title for the dialog. + + + A lazy-loaded dialog. Set 'data-type="dialog-opener"' and 'data-dialog-id="${dialogId}"' + on your button to open the dialog. + + + + + + + + + + diff --git a/src/main/js/components/dialogs/index.js b/src/main/js/components/dialogs/index.js index 65907fd3f3..f5b771910b 100644 --- a/src/main/js/components/dialogs/index.js +++ b/src/main/js/components/dialogs/index.js @@ -292,6 +292,36 @@ function init() { return dialog.show(); }, }; + + behaviorShim.specify( + "[data-type='dialog-opener']", + "-dialog-", + 1000, + (element) => { + element.addEventListener("click", () => { + const templateId = element.dataset.dialogId + "-template"; + + function render() { + const template = document.querySelector("#" + templateId); + const title = template.dataset.title; + const content = template.content.firstElementChild.cloneNode(true); + + behaviorShim.applySubtree(content, false); + dialog.modal(content, { + maxWidth: "550px", + title: title, + }); + } + + if (document.querySelector("#" + templateId)) { + render(); + return; + } + + renderOnDemand(document.querySelector("." + templateId), render); + }); + }, + ); } export default { init }; diff --git a/src/main/scss/components/_details.scss b/src/main/scss/components/_details.scss index 7099c4c210..38f707bcd3 100644 --- a/src/main/scss/components/_details.scss +++ b/src/main/scss/components/_details.scss @@ -1,16 +1,33 @@ +@use "../abstracts/mixins"; + $icon-size: 1.125rem; .jenkins-details { display: flex; - gap: 0.75rem 1.25rem; + align-items: center; + gap: 0.625rem 1.25rem; flex-wrap: wrap; + button.jenkins-details__item, + a.jenkins-details__item { + @include mixins.item($border: false); + + &::before, + &::after { + inset: 0 -0.5rem; + pointer-events: all; + } + } + &__item { - display: grid; - grid-template-columns: auto 1fr; + display: flex; + align-items: center; + justify-content: center; gap: 0.5rem; font-weight: normal; - color: var(--text-color-secondary); + color: var(--text-color-secondary) !important; + padding: 0; + min-height: 2rem; &__icon { display: flex; @@ -18,7 +35,7 @@ $icon-size: 1.125rem; justify-content: center; align-self: start; width: $icon-size; - height: 1lh; + height: 2rem; svg { width: $icon-size; @@ -34,5 +51,6 @@ $icon-size: 1.125rem; &__separator { color: var(--text-color-secondary); opacity: 0.5; + margin-inline: -0.5rem; } } diff --git a/src/main/scss/components/_dialogs.scss b/src/main/scss/components/_dialogs.scss index ceea1f6cf5..e9cd78c50b 100644 --- a/src/main/scss/components/_dialogs.scss +++ b/src/main/scss/components/_dialogs.scss @@ -1,5 +1,4 @@ -$jenkins-dialog-padding: 1.5rem; -$jenkins-dialog-font-size: 0.9375rem; +$jenkins-dialog-padding: 1.25rem; .jenkins-dialog { border-radius: 1rem; @@ -25,12 +24,13 @@ $jenkins-dialog-font-size: 0.9375rem; } &__title { - font-size: 1.125rem; + font-size: 1rem; font-weight: var(--font-bold-weight); padding: 0 $jenkins-dialog-padding; color: var(--text-color); overflow-wrap: anywhere; text-box: cap alphabetic; + margin-top: 0.5625rem; } &__contents { @@ -38,7 +38,6 @@ $jenkins-dialog-font-size: 0.9375rem; overflow-wrap: break-word; padding: 0 $jenkins-dialog-padding; max-height: 75vh; - font-size: $jenkins-dialog-font-size; color: var(--text-color-secondary); &--modal { @@ -72,7 +71,7 @@ $jenkins-dialog-font-size: 0.9375rem; } &__subtitle { - font-size: 1rem; + font-size: var(--font-size-sm); font-weight: var(--font-bold-weight); color: var(--text-color-secondary); padding: 0; @@ -81,10 +80,11 @@ $jenkins-dialog-font-size: 0.9375rem; &__close-button { position: absolute; - top: $jenkins-dialog-padding - 0.5rem; - right: $jenkins-dialog-padding - 0.5rem; - aspect-ratio: 1; + top: $jenkins-dialog-padding; + right: $jenkins-dialog-padding; padding: 0; + width: 2rem; + min-height: 2rem; border-radius: 100%; } diff --git a/src/main/scss/form/_layout.scss b/src/main/scss/form/_layout.scss index ba557f6db5..5e56c13bcd 100644 --- a/src/main/scss/form/_layout.scss +++ b/src/main/scss/form/_layout.scss @@ -87,8 +87,19 @@ border-radius: calc(var(--form-input-border-radius) - 0.25rem); } + &--full-width { + display: grid; + grid-template-columns: 1fr auto; + } + &--monospace { font-family: var(--font-family-mono); + + // Copy buttons' tooltips inherit font-family from the parent element, + // so override it + span { + font-family: var(--font-family-sans); + } } } diff --git a/src/main/scss/pages/_build.scss b/src/main/scss/pages/_build.scss index aca38ec5fb..dd6ba3a6a3 100644 --- a/src/main/scss/pages/_build.scss +++ b/src/main/scss/pages/_build.scss @@ -1,4 +1,5 @@ @use "../base/breakpoints"; +@use "../components/dialogs"; .build-caption-progress-container { display: flex; @@ -107,3 +108,13 @@ font-size: var(--font-size-sm); } } + +.app-parameters-dialog { + display: flex; + flex-direction: column; + gap: dialogs.$jenkins-dialog-padding; + + & > * { + margin-bottom: 0; + } +}