Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move the 'Update' and 'Install' buttons to the app bar #8025

Merged
merged 13 commits into from Jul 10, 2023
19 changes: 19 additions & 0 deletions core/src/main/resources/hudson/PluginManager/_table.js
Expand Up @@ -482,6 +482,25 @@ window.addEventListener("load", function () {
});
}

// Enable/disable the 'Update' button depending on if any updates are checked
const anyCheckboxesSelected = () => {
return (
document.querySelectorAll("input[type='checkbox']:checked:not(:disabled)")
.length > 0
);
};
const updateButton = document.querySelector("#button-update");
const checkboxes = document.querySelectorAll(
"input[type='checkbox'], [data-select], .jenkins-table__checkbox"
);
checkboxes.forEach((checkbox) => {
checkbox.addEventListener("click", () => {
setTimeout(() => {
updateButton.disabled = !anyCheckboxesSelected();
});
});
});

// Show update center error if element exists
const updateCenterError = document.querySelector("#update-center-error");
if (updateCenterError) {
Expand Down
33 changes: 22 additions & 11 deletions core/src/main/resources/hudson/PluginManager/available.jelly
Expand Up @@ -43,13 +43,34 @@ THE SOFTWARE.
value="${request.getParameter('filter')}" />
</div>
<div class="jenkins-app-bar__controls">
<l:isAdmin>
<div class="jenkins-split-button">
<button id="button-install" form="form" type="submit" name="dynamicLoad" class="jenkins-button jenkins-button--primary">
<div class="jenkins-dropdown__item__icon">
<l:icon src="symbol-download" />
Copy link
Member

Choose a reason for hiding this comment

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

using download icon to me is confusing as I (the user) am not downloading anything to my machine.
Rather Jenkins is installing - which is normally an arrow pointing right or the like.

</div>
${%Install}
</button>
<l:overflowButton id="button-install-after-restart"
icon="symbol-chevron-down"
tooltip="${null}"
clazz="jenkins-button--primary">
<button form="form" type="submit" class="jenkins-dropdown__item">
<div class="jenkins-dropdown__item__icon">
<l:icon src="symbol-download" />
</div>
${%Install after restart}
</button>
</l:overflowButton>
</div>
</l:isAdmin>
<st:include page="check.jelly"/>
</div>
</div>

<script src="${resURL}/jsbundles/plugin-manager-ui.js" type="text/javascript"/>

<form method="post" action="install">
<form id="form" method="post" action="install">
<table id="plugins" class="jenkins-table sortable"
data-hasAdmin="${h.hasPermission(app.ADMINISTER)}">
<thead>
Expand All @@ -65,16 +86,6 @@ THE SOFTWARE.
</thead>
<tbody/>
</table>

<div id="bottom-sticker">
<div class="bottom-sticker-inner">
<l:isAdmin>
<f:submit value="${%Install without restart}" name="dynamicLoad"/>
<span style="margin-left:2em;"></span>
<f:submit value="${%Download now and install after restart}"/>
</l:isAdmin>
</div>
</div>
</form>
</l:main-panel>
</l:layout>
Expand Down
22 changes: 10 additions & 12 deletions core/src/main/resources/hudson/PluginManager/updates.jelly
Expand Up @@ -46,13 +46,21 @@ THE SOFTWARE.
value="${request.getParameter('filter')}" />
</div>
<div class="jenkins-app-bar__controls">
<st:include page="check.jelly"/>
<l:isAdmin>
<j:if test="${!empty(list)}">
<button id="button-update" form="form" type="submit" class="jenkins-button jenkins-button--primary" disabled="true">
<l:icon src="symbol-download" />
${%Update}
</button>
</j:if>
</l:isAdmin>
<st:include page="check.jelly" />
</div>
</div>

<st:adjunct includes="hudson.PluginManager._table"/>

<form method="post" action="install">
<form id="form" method="post" action="install">
<j:set var="cache" value="${it.createCache()}"/>

<table id="plugins" class="jenkins-table sortable">
Expand Down Expand Up @@ -232,16 +240,6 @@ THE SOFTWARE.
</j:otherwise>
</j:choose>
</table>

<div id="bottom-sticker">
<div class="bottom-sticker-inner">
<l:isAdmin>
<j:if test="${!empty(list)}">
<f:submit value="${%Download now and install after restart}"/>
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this functionally removed?

</j:if>
</l:isAdmin>
</div>
</div>
<d:invokeBody/>
</form>
</l:main-panel>
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/resources/lib/layout/overflowButton.jelly
Expand Up @@ -37,10 +37,14 @@ THE SOFTWARE.
<st:attribute name="clazz">
Additional CSS class names for the button
</st:attribute>
<st:attribute name="id">
Optional ID for the button
</st:attribute>
</st:documentation>

<button class="jenkins-button ${attrs.clazz}"
tooltip="${attrs.containsKey('tooltip') ? attrs.tooltip : '%More actions'}"
id="${attrs.id}"
type="button" data-dropdown="true">
<j:choose>
<j:when test="${attrs.containsKey('icon')}">
Expand Down
26 changes: 26 additions & 0 deletions war/src/main/js/plugin-manager-ui.js
Expand Up @@ -46,6 +46,8 @@ function applyFilter(searchQuery) {
});

tbody.insertAdjacentHTML("beforeend", rows);

updateInstallButtonState();
}
);
}
Expand Down Expand Up @@ -78,3 +80,27 @@ document.addEventListener("DOMContentLoaded", function () {
);
}
});

function updateInstallButtonState() {
// Enable/disable the 'Install' button depending on if any plugins are checked
const anyCheckboxesSelected = () => {
return (
document.querySelectorAll("input[type='checkbox']:checked").length > 0
);
};
const installButton = document.querySelector("#button-install");
const installAfterRestartButton = document.querySelector(
"#button-install-after-restart"
);
installButton.disabled = true;
installAfterRestartButton.disabled = true;
const checkboxes = document.querySelectorAll("input[type='checkbox']");
checkboxes.forEach((checkbox) => {
checkbox.addEventListener("click", () => {
setTimeout(() => {
installButton.disabled = !anyCheckboxesSelected();
installAfterRestartButton.disabled = !anyCheckboxesSelected();
});
});
});
}
39 changes: 20 additions & 19 deletions war/src/main/scss/abstracts/mixins.scss
Expand Up @@ -70,31 +70,32 @@
box-shadow: 0 0 0 0.66rem transparent;
}

&:hover,
&:focus {
&::before {
background-color: var(--item-background--hover);
&:not(:disabled) {
&:hover,
&:focus-visible {
&::before {
background-color: var(--item-background--hover);
}
}
}

&:active,
&:focus {
outline: none !important;
z-index: 1;
&:active {
outline: none !important;
z-index: 1;

&::before {
background-color: var(--item-background--active);
}
&::before {
background-color: var(--item-background--active);
}

&::after {
box-shadow: 0 0 0 0.33rem var(--item-box-shadow--focus);
&::after {
box-shadow: 0 0 0 0.33rem var(--item-box-shadow--focus);
}
}
}

&:focus-visible {
&::after {
box-shadow: 0 0 0 0.2rem var(--text-color) !important;
opacity: 1 !important;
&:focus-visible {
&::after {
box-shadow: 0 0 0 0.2rem var(--text-color) !important;
opacity: 1 !important;
}
}
}
}
103 changes: 66 additions & 37 deletions war/src/main/scss/modules/buttons.scss
Expand Up @@ -25,6 +25,7 @@
min-height: 36px;
white-space: nowrap;
gap: 1ch;
transition: var(--standard-transition);

&::before {
background: var(--button-background);
Expand All @@ -36,9 +37,9 @@
}

&:disabled {
pointer-events: none;
opacity: 0.5;
filter: saturate(0.6);
cursor: not-allowed;
}
}

Expand All @@ -55,21 +56,23 @@
opacity: 0;
}

&:hover {
&::before {
opacity: 0.9;
&:not(:disabled) {
&:hover {
&::before {
opacity: 0.9;
}
}
}

&:active,
&:focus {
&::before {
opacity: 0.8;
}
&:active,
&:focus-visible {
&::before {
opacity: 0.8;
}

&::after {
box-shadow: 0 0 0 0.33rem var(--primary);
opacity: 0.2;
&::after {
box-shadow: 0 0 0 0.33rem var(--primary);
opacity: 0.2;
}
}
}
}
Expand All @@ -96,21 +99,22 @@
opacity: 0;
}

&:hover {
&::before {
opacity: 0.15;
&:not(:disabled) {
&:hover {
&::before {
opacity: 0.15;
}
}
}

&:active,
&:focus {
&::before {
opacity: 0.2;
}
&:active {
&::before {
opacity: 0.2;
}

&::after {
box-shadow: 0 0 0 0.33rem currentColor;
opacity: 0.1;
&::after {
box-shadow: 0 0 0 0.33rem currentColor;
opacity: 0.1;
}
}
}
}
Expand All @@ -129,21 +133,23 @@
opacity: 0;
}

&:hover {
&::before {
opacity: 0.9;
&:not(:disabled) {
&:hover {
&::before {
opacity: 0.9;
}
}
}

&:active,
&:focus {
&::before {
opacity: 0.8;
}
&:active,
&:focus-visible {
&::before {
opacity: 0.8;
}

&::after {
box-shadow: 0 0 0 0.33rem var(--color);
opacity: 0.3;
&::after {
box-shadow: 0 0 0 0.33rem var(--color);
opacity: 0.3;
}
}
}
}
Expand Down Expand Up @@ -269,3 +275,26 @@
}
}
}

$jenkins-split-button-border-radius: 0.2rem;

.jenkins-split-button {
display: flex;
gap: 1px;

& > :first-child {
border-top-right-radius: $jenkins-split-button-border-radius;
border-bottom-right-radius: $jenkins-split-button-border-radius;
}

& > .jenkins-button:last-of-type {
padding: 0 5px;
border-top-left-radius: $jenkins-split-button-border-radius;
border-bottom-left-radius: $jenkins-split-button-border-radius;

svg {
width: 0.8rem;
height: 0.8rem;
}
}
}