Rewrite the build history widget (#9148)

* Squashed commit of the following:

commit 1ce4cb39c7df313ad000225f1f06c21d9d55d7d8
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Tue Apr 9 09:22:36 2024 +0100

    Rename classes

commit 107d7947874a974fc16df00dcdef46a4d55ed0bc
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Tue Apr 9 08:59:54 2024 +0100

    Update HistoryWidget.java

commit 13575bc27caaf902c6685e8dfdb8ecd2ffc6e64a
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Tue Apr 9 08:59:29 2024 +0100

    Hide buttons

commit e5de5465814c810f9bb09722a00727f73b1158b9
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Tue Apr 9 08:39:04 2024 +0100

    Rename classes

commit 47c84afcd1c4e45f1e202a1a38fc68ef9ec8c702
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Mon Apr 8 23:21:36 2024 +0100

    Add animation

commit e7432b693357b800b91bf355a610fbd3ab62223e
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Mon Apr 8 23:03:19 2024 +0100

    Add navigation buttons

commit 448a09419a0706bd83fb73b555ef55c54916da13
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Mon Apr 8 22:29:32 2024 +0100

    Update _dashboard.scss

commit bb85734be0884e7cb244b13267970722db2f8c05
Merge: c451a7223c 27433f19f3
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Mon Apr 8 22:23:38 2024 +0100

    Merge branch 'master' into new-build-history-2

commit c451a7223cfc68651d11c8f83134c0ee000db363
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Mon Apr 8 13:17:05 2024 +0100

    Update _job.scss

commit 960b162dcd95715f6d1567f57d53fcf79591966d
Merge: d020eb66e5 af655e3fdc
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Mon Apr 8 13:16:54 2024 +0100

    Merge branch 'remove-table-usage' into new-build-history-2

commit af655e3fdc
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Mon Apr 8 13:13:38 2024 +0100

    Init

commit d020eb66e54a63c880dc3c335485aff92c86aaad
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Mon Apr 8 13:09:57 2024 +0100

    Update card.jelly

commit 81bc1d45f17adb021d67c15531d51b8421f78132
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Mon Apr 8 13:07:41 2024 +0100

    Update _buttons.scss

commit 4023460b9aa9243f45870e98ca2299a91263bcce
Merge: 875fb8fb4b da5f593fb0
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Mon Apr 8 13:03:18 2024 +0100

    Merge branch 'master' into new-build-history-2

commit 875fb8fb4baa86c45284d71ee6f5a2a39af68aa0
Merge: 2dc9964871 fe60facf91
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sat Apr 6 19:29:20 2024 +0100

    Merge branch 'master' into new-build-history-2

commit 2dc9964871734a6e8386392d32a13366bc3752e9
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Fri Apr 5 22:17:08 2024 +0100

    Reset files

commit 2da1e14f046e518bc9c22050ddf9f394d65bbc31
Merge: dce466a846 b9fac75ac8
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Fri Apr 5 22:14:46 2024 +0100

    Merge branch 'master' into new-build-history-2

commit dce466a846b5e7bcad40625c8c5bcbd6c6477cf8
Merge: 935c16e1c1 0eed048866
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Wed Feb 28 13:55:35 2024 +0000

    Merge branch 'master' into new-build-history-2

commit 935c16e1c1033c8e33fff49b0e47f9a47615f540
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sun Feb 18 17:54:32 2024 +0000

    Update entry.jelly

commit 2a19a045970d65e446e4ce894d769fb27bdfd921
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sun Feb 18 17:51:58 2024 +0000

    Rename classes

commit 3ef46abe52ba75573bb4e634ad3baad95c2b07ba
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sun Feb 18 17:29:25 2024 +0000

    Update index.jelly

commit da0b2126cdc5f959905ae7eba514a68f891f6620
Merge: 281fac04c0 9d9e2ab467
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sun Feb 18 17:27:12 2024 +0000

    Merge branch 'revamp-dropdowns' into new-build-history-2

commit 281fac04c00e25ab05ff21c71ab30b263d0e3b9b
Merge: 9c69bd02be a6423541f0
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sun Feb 18 17:19:14 2024 +0000

    Merge branch 'master' into new-build-history-2

commit 9d9e2ab467
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sat Feb 10 19:39:24 2024 +0000

    Update templates.js

commit 1c19e431ce
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sat Feb 10 19:38:13 2024 +0000

    Add clazz

commit 8b944e9e3f
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sat Feb 10 15:19:30 2024 +0000

    Update utils.js

commit 069fefbe36
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sat Feb 10 13:23:16 2024 +0000

    Linting

commit 7270712249
Merge: 9865811d38 a6423541f0
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sat Feb 10 12:20:02 2024 +0000

    Merge branch 'master' into revamp-dropdowns

commit 9865811d38
Merge: 1e22c34016 86d39dd23b
Author: Mark Waite <mark.earl.waite@gmail.com>
Date:   Thu Jan 18 05:39:13 2024 -0700

    Merge branch 'master' into revamp-dropdowns

commit 9c69bd02bebcc13b695f112952a0ec97b297a8bb
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sun Jan 14 13:56:49 2024 +0000

    Push

commit 347e966aebbe013f171329c4fb00dee4acd27b33
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sun Jan 14 13:53:52 2024 +0000

    Update filter-build-history.js

commit 0b4a5dd5f4834824d70324b0698f9194a2ffb64b
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sun Jan 14 13:49:54 2024 +0000

    Renam

commit a8277bf932fc3ff5c99ec9f3496c05ed977e7990
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sun Jan 14 13:46:53 2024 +0000

    Fix

commit 855bf1317e8d7187cf4b2016f8571379c841ba91
Merge: 61b0a87e6c 1eb29a8792
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sun Jan 14 13:38:51 2024 +0000

    Merge branch 'master' into new-build-history-2

commit 1e22c34016
Merge: 44981c2a66 48661db9d1
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sun Jan 14 13:33:14 2024 +0000

    Merge branch 'master' into revamp-dropdowns

commit 44981c2a66
Merge: 0075375c9a 1eb29a8792
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Mon Jan 8 21:14:51 2024 +0000

    Merge branch 'master' into revamp-dropdowns

commit 0075375c9a
Merge: 2dd9e32fab 78cdaa9f29
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Thu Jan 4 13:26:24 2024 +0000

    Merge branch 'master' into revamp-dropdowns

commit 2dd9e32fab
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Thu Jan 4 13:24:53 2024 +0000

    Remove translations

commit 6800c8886a
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Thu Jan 4 13:16:19 2024 +0000

    Update header.jelly

commit 1c3961bb64
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Thu Jan 4 13:15:49 2024 +0000

    Add additional docs

commit 163be529e7
Merge: 4cc43e47f9 444f2de993
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Wed Jan 3 21:22:20 2024 +0000

    Merge branch 'master' into revamp-dropdowns

commit 61b0a87e6c3412496e65269e20055d6123d9adb8
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Thu Dec 14 19:38:45 2023 +0000

    More

commit dcd6aaa54576bd1524d071b4cbe75d51aa4244f8
Merge: 0c40b9f3fc edce488000
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Thu Dec 14 19:38:25 2023 +0000

    Merge branch 'stop-button' into new-build-history-2

commit edce488000
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Thu Dec 14 16:06:00 2023 +0000

    Tidy up

commit 157ba0b5bc
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Thu Dec 14 16:04:46 2023 +0000

    Fix i18n

commit a112bd90c1
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Wed Dec 13 22:57:17 2023 +0000

    Update _buttons.scss

commit 91751cf650
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Wed Dec 13 22:39:18 2023 +0000

    Update executors.jelly

commit cd89aeabf4
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Wed Dec 13 22:31:06 2023 +0000

    Fixes

commit 1384091663
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Wed Dec 13 22:28:54 2023 +0000

    Init

commit 0c40b9f3fcb4f12fba2f0e0d45bffd94bb1e196b
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Wed Dec 13 16:50:53 2023 +0000

    Update _buttons.scss

commit 50fe8fc74312296ba6ae1b4a43f6a67680846970
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Wed Dec 13 13:13:10 2023 +0000

    add view transitions

commit d1fd7a9a93b241e93b18ed97af696bd32997d8b7
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Tue Dec 12 20:25:44 2023 +0000

    Tidy up

commit 512b9a1baaa8482aa597785a92e6183316f60030
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Tue Dec 12 19:29:16 2023 +0000

    Push

commit 4f1569079c353c0eddd5f665867d4239a35d8797
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Mon Dec 11 23:30:09 2023 +0000

    push

commit ad75b0f10f5afae5b61e2ac97d44b51ede5801a4
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Mon Dec 11 23:24:11 2023 +0000

    Update _buttons.scss

commit cec222e3bdd9104095abfaa82f07e953fa69d097
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Mon Dec 11 23:23:52 2023 +0000

    Update _buttons.scss

commit 4cc43e47f9
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sun Dec 10 16:04:28 2023 +0000

    Add docs

commit a4c7f4f28c
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sun Dec 10 16:00:23 2023 +0000

    Update taglib

commit c01db44de3
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sun Dec 10 15:56:50 2023 +0000

    Init

commit 21bb3f4262cf65d30c48b76dc43b16a424d357ca
Merge: d3e2920434 428d0e560a
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sun Dec 10 13:22:00 2023 +0000

    Merge branch 'restyle-cards' into new-build-history-2

commit 428d0e560a
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Thu Dec 7 20:17:10 2023 +0000

    Lower weight

commit ac5c255530
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Thu Dec 7 20:15:28 2023 +0000

    Remove more bold weights

commit 2922a690bb
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Thu Dec 7 20:11:33 2023 +0000

    Update _style.scss

commit 9657d460ac
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Thu Dec 7 20:06:16 2023 +0000

    Init

commit d3e2920434d44a45e968392c7617ad1ba140a062
Merge: a2bd9a08b2 cabc8f67b9
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Thu Dec 7 19:50:43 2023 +0000

    Merge branch 'master' into new-build-history-2

commit a2bd9a08b2679cc706f89cd12908197bf0244b13
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sun Nov 26 11:58:22 2023 +0000

    Fixes

commit 7cda5049b75bc7e7d7dcc8516ac6b3983d17b3b0
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sat Nov 25 12:28:04 2023 +0000

    Working build

commit abd994ebe9680f846e13029faa44a2119c861345
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sat Nov 25 11:58:42 2023 +0000

    Working build

commit 9b6defbf444b35127a81bb4cfd298f7648704e49
Merge: 29fcb64a74 7a0e57e35c
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sat Nov 25 11:51:24 2023 +0000

    Merge branch 'progress-bar-new' into new-build-history-2

commit 29fcb64a74e37b445cde8b1847a6be988ad3b85c
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sat Nov 25 11:51:10 2023 +0000

    More

commit 2f946d3f6494223351fc9da3a98654b9a666f766
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sat Nov 25 09:17:06 2023 +0000

    Update _buttons.scss

commit f5474b33429ff8615c74e7be0cf622904c177a65
Merge: 00c3879a27 982bc48fa0
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sat Nov 25 09:16:14 2023 +0000

    Merge branch 'use-symbols-for-build-status-new' into new-build-history-2

commit 982bc48fa0
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sat Nov 25 09:15:56 2023 +0000

    Fix app bar build status icon being incorrect

commit 00c3879a2740e40d81d65463028c82f32782fecf
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sat Nov 25 09:12:10 2023 +0000

    Fixes

commit a4960e9f2c9b9c17ec6f85fd24ef98ca7362455e
Merge: d28aada2e1 c6f5db0be7
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sat Nov 25 08:56:01 2023 +0000

    Merge branch 'use-symbols-for-build-status-new' into new-build-history-2

commit d28aada2e1d5434a542d95afadc4c582a691319a
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sat Nov 25 08:55:18 2023 +0000

    Update _buttons.scss

commit 1d24a19982838f50a6aa6b4510c0fe1090eef364
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Sat Nov 25 08:52:58 2023 +0000

    Init

commit 7a0e57e35c71073258a5635ebba1a87d5456aeda
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Fri Nov 24 21:19:23 2023 +0000

    More

commit 67d4264da9a9ff8539ea74f7462b111d7da46f82
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Fri Nov 24 21:17:09 2023 +0000

    Update _spinner.scss

commit 9befc76209eede89b3e3fd9d124c6ac92ee75c81
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Fri Nov 24 21:07:55 2023 +0000

    Update _spinner.scss

commit 528b46acb3be57f43cf18f09a46cd7caa4ba2120
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Fri Nov 24 21:01:06 2023 +0000

    More

commit ea0c4878137141193ece81a1970007f34e603478
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Fri Nov 24 20:36:53 2023 +0000

    Init

commit c6f5db0be7
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Fri Nov 24 17:52:28 2023 +0000

    Fix icon position

commit 18a84076b5
Merge: aea4d9786e a9c34d7393
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Fri Nov 24 09:58:54 2023 +0000

    Merge branch 'master' into use-symbols-for-build-status-new

commit aea4d9786e
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Fri Nov 24 09:58:29 2023 +0000

    Rename ID

commit 5f76f3840c
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com>
Date:   Mon Nov 20 16:00:14 2023 +0000

    Init

* Update _job.scss

* Lint

* Update filter-build-history.js

* Hide controls if not navigable

* Update RunTest.java

* Add basic JSDoc

* Use dataset rather than attributes

* Update filter-build-history.js

* Rename JS

* Update RunTest.java

* Update jenkins-test-harness version (thanks again Tim!)

* Update _job.scss

* Remove unused SCSS

* Update AbstractScmTagActionTest.java

* Update builds-card.js

* Update builds-card.js

* Rewrite SCSS/Jelly to handle more complex scenarios

* Lint

* Tidy up

* Update _side-panel-widgets.scss

* Add background to card

* Fix lint

* Update queue-items.jelly

* Fix cancel button

* Update queue-items.jelly

* Use debounce properly and adjust time to make it smoother

* Use jenkins-hidden

* Replace TODOs with comments, fix card controls showing when there are no builds

* Add hidden text for previous/next buttons

* Update builds-card.js

* Add data-tooltip-append-to-parent="true" to tooltips

* Wrap badges

* Fix relative expandable link

* Use chevron-down rather than menu icon

---------

Co-authored-by: Daniel Beck <1831569+daniel-beck@users.noreply.github.com>
Co-authored-by: Tim Jacomb <21194782+timja@users.noreply.github.com>
Co-authored-by: Tim Jacomb <timjacomb1@gmail.com>
This commit is contained in:
Jan Faracik 2024-06-14 14:21:23 +01:00 committed by GitHub
parent 9459e1e061
commit 0419465f4d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 684 additions and 1149 deletions

View File

@ -26,10 +26,19 @@ THE SOFTWARE.
Render build histories.
-->
<?jelly escape-by-default='true'?>
<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" xmlns:i="jelly:fmt">
<j:set target="${it}" property="nextBuildNumberToFetch" value="${it.nextBuildNumber}"/>
<!-- build history -->
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:i="jelly:fmt">
<j:set target="${it}" property="nextBuildNumberToFetch" value="${it.nextBuildNumber}" />
<j:invokeStatic className="java.time.LocalDate" method="now" var="now" />
<j:forEach var="pageEntry" items="${it.runs}">
<i:formatDate value="${pageEntry.entry.timestamp.time}" var="date" type="date" dateStyle="long" />
<i:formatDate value="${pageEntry.entry.timestamp.time}" var="simpleDate" type="date" pattern="YYYY-MM-dd" />
<j:if test="${pastDate != date}">
<span class="app-builds-container__heading">${simpleDate == now ? "%Today" : date}</span>
<j:set var="pastDate" value="${date}" />
</j:if>
<st:include page="/hudson/widgets/HistoryWidget/entry.jelly" />
</j:forEach>
</j:jelly>
</j:jelly>

View File

@ -34,54 +34,58 @@ THE SOFTWARE.
<j:if test="${h.isUserTimeZoneOverride()}">
<i:setTimeZone value="${h.getUserTimeZone()}" />
</j:if>
<tr class="build-row ${transitive} single-line" page-entry-id="${pageEntry.entryId}">
<td class="build-row-cell">
<div class="pane build-name">
<div class="build-icon">
<a class="build-status-link" href="${h.getConsoleUrl(build)}" tooltip="${build.iconColor.description} > ${%Console Output}">
<l:icon src="symbol-status-${build.iconColor.iconName}" />
</a>
</div>
<a class="model-link inside build-link display-name" update-parent-class=".build-row" href="${link}">${build.displayName}</a>
</div>
<div class="pane build-details" time="${build.timestamp.time.time}">
<j:set var="linkTitleAttr" value=""/>
<j:if test="${!build.building}">
<j:set var="linkTitleAttr">${%Took} ${build.durationString}</j:set>
</j:if>
<a class="model-link inside build-link" href="${link}" update-parent-class=".build-row" tooltip="${linkTitleAttr}">
<i:formatDate value="${build.timestamp.time}" type="both" dateStyle="medium" timeStyle="short" /> ${h.getUserTimeZonePostfix(build.timestamp.time)}
</a>
<j:if test="${build.building}">
<j:set target="${it.widget}" property="nextBuildNumberToFetch" value="${build.number}"/>
<t:buildProgressBar build="${build}"/>
</j:if>
</div>
<div class="pane build-controls">
<div class="middle-align build-badge">
<j:set var="badges" value="${build.badgeActions}"/>
<j:if test="${!empty(badges)}">
<st:nbsp/>
<j:forEach var="badge" items="${badges}">
<st:include it="${badge}" page="badge.jelly" />
</j:forEach>
<div class="app-builds-container__item" page-entry-id="${pageEntry.entryId}">
<a class="app-builds-container__item__icon"
href="${h.getConsoleUrl(build)}"
tooltip="${build.iconColor.description}"
data-tooltip-append-to-parent="true">
<l:icon src="symbol-status-${build.iconColor.iconName}" />
</a>
<div class="app-builds-container__item__inner">
<a href="${link}" class="app-builds-container__item__inner__link">
${build.displayName}
<span class="app-builds-container__item__time" time="${build.timestamp.time.time}">
<j:set var="linkTitleAttr" value="${null}" />
<j:if test="${!build.building}">
<j:set var="linkTitleAttr">${%Took} ${build.durationString}</j:set>
</j:if>
</div>
<j:if test="${build.building}">
<div class="build-stop">
<!-- Check CANCEL permission for Project, Admin permission otherwise -->
<j:if test="${empty(it.widget.owner.CANCEL) ? h.hasPermission(app.ADMINISTER) : it.widget.owner.hasPermission(it.widget.owner.CANCEL)}">
<l:stopButton href="${link}stop" alt="${%Cancel}" confirm="${%confirm(build.fullDisplayName)}" />
</j:if>
<div tooltip="${linkTitleAttr}" data-tooltip-append-to-parent="true">
<i:formatDate value="${build.timestamp.time}" type="time" timeStyle="short" />
${h.getUserTimeZonePostfix()}
</div>
</span>
</a>
<div class="app-builds-container__item__inner__controls">
<j:if test="${build.building}">
<j:if test="${build.building}">
<j:set target="${it.widget}" property="nextBuildNumberToFetch" value="${build.number}"/>
<t:buildProgressBar build="${build}"/>
</j:if>
<!-- Check CANCEL permission for Project, Admin permission otherwise -->
<j:if test="${empty(it.widget.owner.CANCEL) ? h.hasPermission(app.ADMINISTER) : it.widget.owner.hasPermission(it.widget.owner.CANCEL)}">
<l:stopButton href="${link}stop" alt="${%Cancel}" confirm="${%confirm(build.fullDisplayName)}" />
</j:if>
</j:if>
<j:set var="badges" value="${build.badgeActions}"/>
<j:if test="${!empty(badges)}">
<j:forEach var="badge" items="${badges}">
<st:include it="${badge}" page="badge.jelly" />
</j:forEach>
</j:if>
</div>
<j:if test="${!empty build.truncatedDescription}">
<div class="pane desc indent-multiline">
<j:out value="${app.markupFormatter.translate(build.truncatedDescription)}"/>
</div>
</j:if>
<div class="left-bar" />
</td>
</tr>
</div>
<button class="jenkins-card__reveal jenkins-jumplist-link" data-href="${link}">
<l:icon src="symbol-chevron-down" />
</button>
<j:if test="${!empty build.truncatedDescription}">
<div class="app-builds-container__item__description">
<j:out value="${app.markupFormatter.translate(build.truncatedDescription)}"/>
</div>
</j:if>
</div>
</j:jelly>

View File

@ -23,76 +23,54 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<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" xmlns:i="jelly:fmt">
<j:parse var="paneTitle">
<j:invokeStatic var="currentThread" className="java.lang.Thread" method="currentThread"/>
<j:invoke var="jobClass" on="${currentThread.contextClassLoader}" method="loadClass">
<j:arg value="hudson.model.Job"/>
</j:invoke>
<div class="jenkins-pane__header--build-history">
<j:if test="${jobClass.isAssignableFrom(it.owner.class)}">
<t:buildHealth job="${it.owner}" iconSizeClass="icon-sm" link="${it.baseUrl}/lastBuild"/>
</j:if>
${it.displayName}
<j:if test="${jobClass.isAssignableFrom(it.owner.class)}">
<a href="${it.baseUrl}/buildTimeTrend">${%trend}</a>
</j:if>
</div>
</j:parse>
<j:parse var="paneFooter">
<!--
RSS link
-->
<span class="build-rss-links">
<a class="build-rss-all-link" href="${it.baseUrl}/rssAll">
<span class="build-rss-all-icon">
<l:icon src="symbol-rss" />
</span>
Atom feed ${%for all}
</a>
<a class="build-rss-failed-link" href="${it.baseUrl}/rssFailed">
<span class="build-rss-failed-icon">
<l:icon src="symbol-rss" />
</span>
Atom feed ${%for failures}
</a>
</span>
</j:parse>
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:dd="/lib/layout/dropdowns">
<script src="${resURL}/jsbundles/pages/project/builds-card.js" type="text/javascript" defer="true" />
<j:set var="page" value="${it.historyPageFilter}" />
<div id="buildHistoryPage" page-ajax="${it.baseUrl}/buildHistory/ajax" page-entry-newest="${page.newestOnPage}" page-entry-oldest="${page.oldestOnPage}" page-has-up="${page.hasUpPage}" page-has-down="${page.hasDownPage}">
<div id="buildHistoryPageNav">
<div class="buildHistoryPageNav__item buildHistoryPageNav__item--page-one pageOne" title="Page 1 (Newest/Latest Builds)">
<div class="buildHistoryPageNav__item-page-one-top"></div>
<l:svgIcon href="${resURL}/images/svgs/go-up.svg#arrow" viewBox="0 0 16 16" />
<j:set var="controls">
<l:overflowButton icon="symbol-menu" clazz="jenkins-card__reveal">
<dd:item icon="symbol-rss"
text="Atom feed ${%for all}"
href="${it.baseUrl}/rssAll" />
<dd:item icon="symbol-rss"
text="Atom feed ${%for failures}"
href="${it.baseUrl}/rssFailed" />
</l:overflowButton>
</j:set>
<div id="buildHistoryPage" page-ajax="${it.baseUrl}/buildHistory/ajax"
data-page-entry-newest="${page.newestOnPage}"
data-page-entry-oldest="${page.oldestOnPage}"
data-page-has-up="${page.hasUpPage}"
data-page-has-down="${page.hasDownPage}">
<l:card id="jenkins-builds" title="Builds" controls="${controls}" expandable="${it.baseUrl}/buildTimeTrend">
<l:search-bar placeholder="${%find}"
clazz="${page.runs.isEmpty() and page.queueItems.isEmpty() ? 'jenkins-hidden' : ''}"/>
<div class="app-builds-container">
<div id="no-builds" class="app-builds-container__placeholder">
${%No builds}
</div>
<div id="jenkins-build-history" class="app-builds-container__items">
</div>
<div class="app-builds-container__controls" id="controls">
<button class="jenkins-button jenkins-button--tertiary jenkins-card__unveil" id="up">
<l:icon src="symbol-arrow-left" />
<span class="jenkins-visually-hidden">${%Newer builds}</span>
</button>
<button class="jenkins-button jenkins-button--tertiary jenkins-card__unveil" id="down">
<l:icon src="symbol-arrow-right" />
<span class="jenkins-visually-hidden">${%Older builds}</span>
</button>
</div>
</div>
<div class="buildHistoryPageNav__item pageUp" title="Newer Builds">
<l:svgIcon href="${resURL}/images/svgs/go-up.svg#arrow" viewBox="0 0 16 16" />
</div>
<div class="buildHistoryPageNav__item pageDown" title="Older Builds">
<l:svgIcon href="${resURL}/images/svgs/go-down.svg#arrow" viewBox="0 0 16 16" />
</div>
</div>
<l:pane width="3" title="${h.runScript(paneTitle)}" footer="${h.runScript(paneFooter)}" id="buildHistory" class="jenkins-pane stripped">
<tr class="build-search-row">
<td>
<l:search-bar placeholder="${%find}" clazz="${page.runs.isEmpty() and page.queueItems.isEmpty() ? 'jenkins-hidden' : ''}"/>
<div id="no-builds" class="jenkins-pane__information" style="${page.runs.isEmpty() and page.queueItems.isEmpty() ? '' : 'display: none;'}">
${%No builds}
</div>
</td>
</tr>
<st:include page="entries.jelly" it="${page}" />
</l:pane>
<!--The value for `page-next-build` is modified inside of `entries.jelly` on render, so set the attribute-->
<!--after that component has been rendered to get the correct value to use in `filter-build-history.js`-->
<div id="properties" page-next-build="${it.nextBuildNumberToFetch ?: it.owner.nextBuildNumber}"/>
<!--The value for `page-next-build` is modified inside of `entries.jelly` on render, so set the attribute-->
<!--after that component has been rendered to get the correct value to use in `builds-card.js`-->
<div id="properties" page-next-build="${it.nextBuildNumberToFetch ?: it.owner.nextBuildNumber}"/>
</l:card>
</div>
<script src="${resURL}/jsbundles/filter-build-history.js" type="text/javascript"/>
</j:jelly>

View File

@ -1 +1 @@
find=Filter...
find=Filter

View File

@ -22,20 +22,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<!--
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout">
<l:ajax>
<j:choose>
<j:when test="${it.size() > 0}">
<table class="pane hasPageData" page-entry-newest="${it.newestOnPage}" page-entry-oldest="${it.oldestOnPage}" page-has-up="${it.hasUpPage}" page-has-down="${it.hasDownPage}">
<st:include page="entries.jelly" />
</table>
</j:when>
<j:otherwise>
<table class="pane"></table>
</j:otherwise>
</j:choose>
<j:if test="${it.size() > 0}">
<div data-page-entry-newest="${it.newestOnPage}"
data-page-entry-oldest="${it.oldestOnPage}"
data-page-has-up="${it.hasUpPage}"
data-page-has-down="${it.hasDownPage}">
<st:include page="entries.jelly" />
</div>
</j:if>
</l:ajax>
</j:jelly>
</j:jelly>

View File

@ -28,50 +28,43 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout">
<!-- pending build -->
<j:set var="queuedItems" value="${it.queueItems}" />
<j:if test="${!queuedItems.isEmpty()}">
<j:forEach var="pageEntry" items="${queuedItems}" indexVar="i">
<j:set var="item" value="${pageEntry.entry}"/>
<j:set var="id" value="${h.generateId()}"/>
<tr class="build-row build-pending transitive single-line" id="${item.id}" page-entry-id="${pageEntry.entryId}">
<td class="build-row-cell">
<div class="pane build-name">
<div class="build-icon">
<l:icon src="symbol-status-nobuilt" class="icon-sm"/>
</div>
<!-- Don't use math unless needed, in case nextBuildNumber is not numeric -->
<div class="display-name" title="${%Expected build number}">
#${queuedItems.size()==1 ? it.widget.owner.nextBuildNumber
: it.widget.owner.nextBuildNumber+queuedItems.size()-i-1}
</div>
</div>
<div class="pane build-details indent-multiline">
<j:set var="cause" value="${item.getCauseOfBlockage()}"/>
<j:choose>
<j:when test="${cause!=null}">
(${%pending}—<st:include it="${cause}" page="summary.jelly"/>)
</j:when>
<j:otherwise>
(${%pending})
</j:otherwise>
</j:choose>
<j:if test="${!item.params.isEmpty()}">
<div style="float:right;margin-right:10px;" tooltip="Build Parameters:${item.params}" data-tooltip-append-to-parent="true">
<l:icon src="symbol-parameters" class="icon-sm" />
</div>
</j:if>
</div>
<div class="pane build-controls">
<div class="build-stop">
<j:if test="${item.hasCancelPermission()}">
<l:stopButton href="${rootURL}/queue/cancelItem?id=${item.id}" alt="${%Cancel this build}"/>
</j:if>
</div>
</div>
<div class="left-bar"></div>
</td>
</tr>
</j:forEach>
</j:if>
<!-- pending build -->
<j:set var="queuedItems" value="${it.queueItems}"/>
<j:if test="${!queuedItems.isEmpty()}">
<span class="app-builds-container__heading">${%Pending}</span>
<j:forEach var="pageEntry" items="${queuedItems}">
<j:set var="item" value="${pageEntry.entry}"/>
<j:set var="id" value="${h.generateId()}"/>
<div class="app-builds-container__item app-builds-container__item--not-interactable" page-entry-id="${pageEntry.entryId}">
<div class="app-builds-container__item__icon">
<l:icon src="symbol-status-nobuilt" />
</div>
<div class="app-builds-container__item__inner">
<div class="app-builds-container__item__inner__link">
<!-- Don't use math unless needed, in case nextBuildNumber is not numeric -->
#${queuedItems.size() == 1 ? it.widget.owner.nextBuildNumber : it.widget.owner.nextBuildNumber+queuedItems.size()-i-1}
</div>
<div class="app-builds-container__item__inner__controls">
<j:if test="${!item.params.isEmpty()}">
<div tooltip="Build Parameters: ${item.params}" data-tooltip-append-to-parent="true">
<l:icon src="symbol-parameters" class="icon-sm" />
</div>
</j:if>
<j:if test="${item.hasCancelPermission()}">
<l:stopButton href="${rootURL}/queue/cancelItem?id=${item.id}" alt="${%Cancel}"/>
</j:if>
</div>
</div>
<div class="app-builds-container__item__description">
<j:set var="cause" value="${item.getCauseOfBlockage()}"/>
<j:choose>
<j:when test="${cause!=null}">
<st:include it="${cause}" page="summary.jelly"/>
</j:when>
</j:choose>
</div>
</div>
</j:forEach>
</j:if>
</j:jelly>

View File

@ -0,0 +1,58 @@
<!--
The MIT License
Copyright (c) 2024 Jan Faracik
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout">
<st:documentation>
<st:attribute name="title" use="required">
Title of the card
</st:attribute>
<st:attribute name="id">
Optional ID for the card
</st:attribute>
<st:attribute name="controls">
Shows controls in the top right of the card
</st:attribute>
<st:attribute name="expandable">
Shows the expand icon if set, navigates to the value provided on click
</st:attribute>
</st:documentation>
<div class="jenkins-card" id="${attrs.id}">
<div class="jenkins-card__title">
${attrs.title}
<div class="jenkins-card__controls">
<j:out value="${controls}"/>
<j:if test="${attrs.expandable != null}">
<a href="${attrs.expandable}" class="jenkins-card__reveal" tooltip="${%Expand}">
<l:icon src="symbol-expand" />
</a>
</j:if>
</div>
</div>
<div class="jenkins-card__content">
<d:invokeBody />
</div>
</div>
</j:jelly>

View File

@ -157,7 +157,7 @@ public class RunTest {
HtmlPage htmlPage = wc.goTo(upProject.getUrl());
// trigger the tooltip display
htmlPage.executeJavaScript("document.querySelector('#buildHistory table .build-badge svg')._tippy.show()");
htmlPage.executeJavaScript("document.querySelector('#jenkins-build-history .app-builds-container__item__inner__controls svg')._tippy.show()");
wc.waitForBackgroundJavaScript(500);
ScriptResult result = htmlPage.executeJavaScript("document.querySelector('.tippy-content').innerHTML;");
Object jsResult = result.getJavaScriptResult();

View File

@ -66,7 +66,7 @@ public class AbstractScmTagActionTest {
HtmlPage page = wc.getPage(p);
DomElement buildHistory = page.getElementById("buildHistory");
DomElement buildHistory = page.getElementById("buildHistoryPage");
DomNodeList<HtmlElement> imgs = buildHistory.getElementsByTagName("img");
HtmlImage tagImage = (HtmlImage) imgs.stream()
.filter(i -> i.getAttribute("class").contains("icon-save"))

View File

@ -31,7 +31,7 @@ function generateJumplistAccessors() {
*/
function generateDropdowns() {
behaviorShim.specify(
"li.children, #menuSelector, .jenkins-menu-dropdown-chevron",
"li.children, .jenkins-jumplist-link, #menuSelector, .jenkins-menu-dropdown-chevron",
"-dropdown-",
1000,
(element) =>

View File

@ -1,590 +0,0 @@
import debounce from "lodash/debounce";
const buildHistoryContainer = document.getElementById("buildHistory");
const pageSearchInputContainer = buildHistoryContainer.querySelector(
".build-search-row .jenkins-search",
);
const pageSearchInput = buildHistoryContainer.querySelector(
".build-search-row input",
);
const buildHistoryPage = document.getElementById("buildHistoryPage");
const properties = document.getElementById("properties");
const ajaxUrl = buildHistoryPage.getAttribute("page-ajax");
const nextBuild = properties.getAttribute("page-next-build");
const noBuildsBanner = document.getElementById("no-builds");
const sidePanel = document.getElementById("side-panel");
const buildHistoryPageNav = document.getElementById("buildHistoryPageNav");
const pageOne = buildHistoryPageNav.querySelector(".pageOne");
const pageUp = buildHistoryPageNav.querySelector(".pageUp");
const pageDown = buildHistoryPageNav.querySelector(".pageDown");
const leftRightPadding = 4;
const updateBuildsRefreshInterval = 5000;
function updateBuilds(params) {
if (isPageVisible()) {
fetch(ajaxUrl + toQueryString(params), {
headers: {
n: buildHistoryContainer.headers[1],
},
}).then((rsp) => {
if (rsp.ok) {
rsp.text().then((responseText) => {
var dataTable = getDataTable(buildHistoryContainer);
var rows = dataTable.rows;
// Check there are no existing rows (except the search bar) before showing the no builds banner
if (
rows.length <= 1 &&
responseText === '<table class="pane"></table>'
) {
noBuildsBanner.style.display = "block";
if (
typeof params === "object" &&
"search" in params &&
params.search !== ""
) {
pageSearchInputContainer.classList.remove("jenkins-hidden");
} else {
pageSearchInputContainer.classList.add("jenkins-hidden");
}
} else {
noBuildsBanner.style.display = "none";
pageSearchInputContainer.classList.remove("jenkins-hidden");
}
//delete rows with transitive data
var firstBuildRow = 0;
if (rows[firstBuildRow].classList.contains("build-search-row")) {
firstBuildRow++;
}
while (
rows.length > 1 &&
rows[firstBuildRow].classList.contains("transitive")
) {
rows[firstBuildRow].remove();
}
// insert new rows
var div = document.createElement("div");
div.innerHTML = responseText;
Behaviour.applySubtree(div);
var pivot = rows[firstBuildRow];
var newDataTable = getDataTable(div);
var newRows = newDataTable.rows;
while (newRows.length > 0) {
if (pivot !== undefined) {
// The data table has rows. Insert before a "pivot" row (first row).
pivot.parentNode.insertBefore(newRows[0], pivot);
} else {
// The data table has no rows. In this case, we just add all new rows directly to the
// table, one after the other i.e. we don't insert before a "pivot" row (first row).
dataTable
.getElementsByTagName("tbody")[0]
.appendChild(newRows[0]);
}
}
if (newDataTable.classList.contains("hasPageData")) {
buildHistoryPage.setAttribute(
"page-entry-newest",
newDataTable.getAttribute("page-entry-newest"),
);
}
// next update
buildHistoryContainer.headers = ["n", rsp.headers.get("n")];
checkAllRowCellOverflows();
createRefreshTimeout(params);
});
}
});
} else {
createRefreshTimeout(params);
}
}
var buildRefreshTimeout;
function createRefreshTimeout(params) {
cancelRefreshTimeout();
buildRefreshTimeout = window.setTimeout(
() => updateBuilds(params),
updateBuildsRefreshInterval,
);
}
function cancelRefreshTimeout() {
if (buildRefreshTimeout) {
window.clearTimeout(buildRefreshTimeout);
buildRefreshTimeout = undefined;
}
}
function hasPageUp() {
return buildHistoryPage.getAttribute("page-has-up") === "true";
}
function hasPageDown() {
return buildHistoryPage.getAttribute("page-has-down") === "true";
}
function getNewestEntryId() {
return buildHistoryPage.getAttribute("page-entry-newest");
}
function getOldestEntryId() {
return buildHistoryPage.getAttribute("page-entry-oldest");
}
function getDataTable(buildHistoryDiv) {
return buildHistoryDiv.querySelector("table.pane");
}
function updatePageParams(dataTable) {
buildHistoryPage.setAttribute(
"page-has-up",
dataTable.getAttribute("page-has-up"),
);
buildHistoryPage.setAttribute(
"page-has-down",
dataTable.getAttribute("page-has-down"),
);
buildHistoryPage.setAttribute(
"page-entry-newest",
dataTable.getAttribute("page-entry-newest"),
);
buildHistoryPage.setAttribute(
"page-entry-oldest",
dataTable.getAttribute("page-entry-oldest"),
);
}
function togglePageUpDown() {
buildHistoryPageNav.classList.remove("hasUpPage");
buildHistoryPageNav.classList.remove("hasDownPage");
if (hasPageUp()) {
buildHistoryPageNav.classList.add("hasUpPage");
}
if (hasPageDown()) {
buildHistoryPageNav.classList.add("hasDownPage");
}
}
function checkRowCellOverflows(row) {
if (!row) {
return;
}
if (row.classList.contains("overflow-checked")) {
// already done.
return;
}
function markSingleline() {
row.classList.add("single-line");
row.classList.remove("multi-line");
}
function markMultiline() {
row.classList.remove("single-line");
row.classList.add("multi-line");
}
function indentMultiline(element) {
element.classList.add("indent-multiline");
}
function blockWrap(el1, el2) {
var div = document.createElement("div");
div.classList.add("block");
div.classList.add("wrap");
el1.classList.add("wrapped");
el2.classList.add("wrapped");
el1.parentNode.insertBefore(div, el1);
el1.parentNode.removeChild(el1);
el2.parentNode.removeChild(el2);
div.appendChild(el1);
div.appendChild(el2);
return div;
}
function blockUnwrap(element) {
element.querySelectorAll(".wrapped").forEach(function (wrappedEl) {
wrappedEl.parentNode.removeChild(wrappedEl);
element.parentNode.insertBefore(wrappedEl, element);
wrappedEl.classList.remove("wrapped");
});
element.parentNode.removeChild(element);
}
var buildName = row.querySelector(".build-name");
var buildDetails = row.querySelector(".build-details");
if (!buildName || !buildDetails) {
return;
}
var buildControls = row.querySelector(".build-controls");
var desc = row.querySelector(".desc");
function resetCellOverflows() {
markSingleline();
// undo block wraps
row.querySelectorAll(".block.wrap").forEach(function (blockWrap) {
blockUnwrap(blockWrap);
});
buildName.classList.remove("block");
buildName.removeAttribute("style");
buildDetails.classList.remove("block");
buildDetails.removeAttribute("style");
if (buildControls) {
buildControls.classList.remove("block");
buildDetails.removeAttribute("style");
}
}
// Undo everything from the previous poll.
resetCellOverflows();
// Mark the text as multiline, if it has more than one line
if (desc) {
markMultiline();
}
var rowWidth = buildHistoryContainer.clientWidth;
var usableRowWidth = rowWidth - leftRightPadding * 2;
var nameOverflowParams = getElementOverflowParams(buildName);
var detailsOverflowParams = getElementOverflowParams(buildDetails);
var controlsOverflowParams;
if (buildControls) {
controlsOverflowParams = getElementOverflowParams(buildControls);
}
function fitToControlsHeight(element) {
if (buildControls) {
if (element.clientHeight < buildControls.clientHeight) {
element.style.height = buildControls.clientHeight.toString() + "px";
}
}
}
function setBuildControlWidths() {
if (buildControls) {
var buildBadge = buildControls.querySelector(".build-badge");
if (buildBadge) {
var buildControlsWidth = buildControls.clientWidth;
var buildBadgeWidth;
var buildStop = buildControls.querySelector(".build-stop");
if (buildStop) {
buildStop.style.width = "24px";
// Minus 24 for the buildStop width,
// minus 4 for left+right padding in the controls container
buildBadgeWidth = buildControlsWidth - 24 - leftRightPadding;
if (buildControls.classList.contains("indent-multiline")) {
buildBadgeWidth = buildBadgeWidth - 20;
}
buildBadge.style.width = buildBadgeWidth + "px";
} else {
buildBadge.style.width = "100%";
}
}
controlsOverflowParams = getElementOverflowParams(buildControls);
}
}
setBuildControlWidths();
var controlsRepositioned = false;
if (nameOverflowParams.isOverflowed || detailsOverflowParams.isOverflowed) {
// At least one of the cells (name or details) needs to move to a row of its own.
markMultiline();
if (buildControls) {
// We have build controls. Lets see can we find a combination that allows the build controls
// to sit beside either the build name or the build details.
var badgesOverflowing = false;
var nameLessThanHalf = true;
var detailsLessThanHalf = true;
var buildBadge = buildControls.querySelector(".build-badge");
if (buildBadge) {
var badgeOverflowParams = getElementOverflowParams(buildBadge);
if (badgeOverflowParams.isOverflowed) {
// The badges are also overflowing. In this case, we will only attempt to
// put the controls on the same line as the name or details (see below)
// if the name or details is using less than half the width of the build history
// widget.
badgesOverflowing = true;
nameLessThanHalf =
nameOverflowParams.scrollWidth < usableRowWidth / 2;
detailsLessThanHalf =
detailsOverflowParams.scrollWidth < usableRowWidth / 2;
}
}
function expandLeftWithRight(
leftCellOverFlowParams,
rightCellOverflowParams,
) {
// Float them left and right...
leftCellOverFlowParams.element.style.float = "left";
rightCellOverflowParams.element.style.float = "right";
if (
!leftCellOverFlowParams.isOverflowed &&
!rightCellOverflowParams.isOverflowed
) {
// If neither left nor right are overflowed, just leave as is and let them float left and right.
return;
}
if (
leftCellOverFlowParams.isOverflowed &&
!rightCellOverflowParams.isOverflowed
) {
leftCellOverFlowParams.element.style.width =
leftCellOverFlowParams.scrollWidth + "px";
return;
}
if (
!leftCellOverFlowParams.isOverflowed &&
rightCellOverflowParams.isOverflowed
) {
rightCellOverflowParams.element.style.width =
rightCellOverflowParams.scrollWidth + "px";
return;
}
}
if (
(!badgesOverflowing || nameLessThanHalf) &&
nameOverflowParams.scrollWidth + controlsOverflowParams.scrollWidth <=
usableRowWidth
) {
// Build name and controls can go on one row (first row). Need to move build details down
// to a row of its own (second row) by making it a block element, forcing it to wrap. If there
// are controls, we move them up to position them after the build name by inserting before the
// build details.
buildDetails.classList.add("block");
buildControls.parentNode.removeChild(buildControls);
buildDetails.parentNode.insertBefore(buildControls, buildDetails);
var wrap = blockWrap(buildName, buildControls);
wrap.classList.add("build-name-controls");
indentMultiline(buildDetails);
nameOverflowParams = getElementOverflowParams(buildName); // recalculate
expandLeftWithRight(nameOverflowParams, controlsOverflowParams);
setBuildControlWidths();
fitToControlsHeight(buildName);
} else if (
(!badgesOverflowing || detailsLessThanHalf) &&
detailsOverflowParams.scrollWidth +
controlsOverflowParams.scrollWidth <=
usableRowWidth
) {
// Build details and controls can go on one row. Need to make the
// build name (first field) a block element, forcing the details and controls to wrap
// onto the next row (creating a second row).
buildName.classList.add("block");
wrap = blockWrap(buildDetails, buildControls);
indentMultiline(wrap);
wrap.classList.add("build-details-controls");
detailsOverflowParams = getElementOverflowParams(buildDetails); // recalculate
expandLeftWithRight(detailsOverflowParams, controlsOverflowParams);
setBuildControlWidths();
fitToControlsHeight(buildDetails);
} else {
// No suitable combo fits on a row. All need to go on rows of their own.
buildName.classList.add("block");
buildDetails.classList.add("block");
buildControls.classList.add("block");
indentMultiline(buildDetails);
indentMultiline(buildControls);
nameOverflowParams = getElementOverflowParams(buildName); // recalculate
detailsOverflowParams = getElementOverflowParams(buildDetails); // recalculate
setBuildControlWidths();
}
controlsRepositioned = true;
} else {
buildName.classList.add("block");
buildDetails.classList.add("block");
indentMultiline(buildDetails);
}
}
if (buildControls && !controlsRepositioned) {
buildBadge = buildControls.querySelector(".build-badge");
if (buildBadge) {
badgeOverflowParams = getElementOverflowParams(buildBadge);
if (badgeOverflowParams.isOverflowed) {
markMultiline();
indentMultiline(buildControls);
buildControls.classList.add("block");
controlsRepositioned = true;
setBuildControlWidths();
}
}
}
if (
!nameOverflowParams.isOverflowed &&
!detailsOverflowParams.isOverflowed &&
!controlsRepositioned
) {
fitToControlsHeight(buildName);
fitToControlsHeight(buildDetails);
}
row.classList.add("overflow-checked");
}
function checkAllRowCellOverflows() {
if (isRunAsTest) {
return;
}
var dataTable = getDataTable(buildHistoryContainer);
var rows = dataTable.rows;
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
checkRowCellOverflows(row);
}
}
function loadPage(params, focusOnSearch) {
var searchString = pageSearchInput.value;
if (searchString !== "") {
if (params === undefined) {
params = {};
}
params.search = searchString;
}
fetch(ajaxUrl + toQueryString(params)).then((rsp) => {
if (rsp.ok) {
rsp.text().then((responseText) => {
pageSearchInputContainer.classList.remove("jenkins-search--loading");
buildHistoryContainer.classList.remove("jenkins-pane--loading");
if (responseText === '<table class="pane"></table>') {
noBuildsBanner.style.display = "block";
if (
typeof params === "object" &&
"search" in params &&
params.search !== ""
) {
pageSearchInputContainer.classList.remove("jenkins-hidden");
} else {
pageSearchInputContainer.classList.add("jenkins-hidden");
}
} else {
noBuildsBanner.style.display = "none";
pageSearchInputContainer.classList.remove("jenkins-hidden");
}
var dataTable = getDataTable(buildHistoryContainer);
var tbody = dataTable.getElementsByTagName("tbody")[0];
var rows = tbody.getElementsByClassName("build-row");
// Delete all build rows
while (rows.length > 0) {
rows[0].remove();
}
// insert new rows
var div = document.createElement("div");
div.innerHTML = responseText;
Behaviour.applySubtree(div);
var newDataTable = getDataTable(div);
var newRows = newDataTable.rows;
while (newRows.length > 0) {
tbody.appendChild(newRows[0]);
}
checkAllRowCellOverflows();
updatePageParams(newDataTable);
togglePageUpDown();
if (!hasPageUp()) {
createRefreshTimeout(params);
}
if (focusOnSearch) {
pageSearchInput.focus();
}
});
}
});
}
const handleFilter = function () {
loadPage({}, true);
};
const debouncedFilter = debounce(handleFilter, 300);
document.addEventListener("DOMContentLoaded", function () {
// Apply correct styling upon filter bar text change, call API after wait
if (pageSearchInput !== null) {
pageSearchInput.addEventListener("input", function () {
pageSearchInputContainer.classList.add("jenkins-search--loading");
buildHistoryContainer.classList.add("jenkins-pane--loading");
noBuildsBanner.style.display = "none";
debouncedFilter();
});
}
if (isRunAsTest) {
return;
}
// If the build history pane is collapsed, just return immediately and don't set up
// the build history refresh.
if (buildHistoryContainer.classList.contains("collapsed")) {
return;
}
buildHistoryContainer.headers = ["n", nextBuild];
createRefreshTimeout();
checkAllRowCellOverflows();
// Show/hide the nav as the mouse moves into the sidepanel and build history.
sidePanel.addEventListener("mouseover", function () {
buildHistoryPageNav.classList.add("mouseOverSidePanel");
});
sidePanel.addEventListener("mouseout", function () {
buildHistoryPageNav.classList.remove("mouseOverSidePanel");
});
buildHistoryContainer.addEventListener("mouseover", function () {
buildHistoryPageNav.classList.add("mouseOverSidePanelBuildHistory");
});
buildHistoryContainer.addEventListener("mouseout", function () {
buildHistoryPageNav.classList.remove("mouseOverSidePanelBuildHistory");
});
pageOne.addEventListener("click", function () {
loadPage();
});
pageUp.addEventListener("click", function () {
loadPage({ "newer-than": getNewestEntryId() });
});
pageDown.addEventListener("click", function () {
if (hasPageDown()) {
cancelRefreshTimeout();
loadPage({ "older-than": getOldestEntryId() });
} else {
// wrap back around to the top
loadPage();
}
});
togglePageUpDown();
});

View File

@ -0,0 +1,142 @@
import debounce from "lodash/debounce";
import behaviorShim from "@/util/behavior-shim";
// Card/item controls
const buildHistoryPage = document.getElementById("buildHistoryPage");
const pageSearch = buildHistoryPage.querySelector(".jenkins-search");
const pageSearchInput = buildHistoryPage.querySelector("input");
const ajaxUrl = buildHistoryPage.getAttribute("page-ajax");
const card = document.querySelector("#jenkins-builds");
const contents = card.querySelector("#jenkins-build-history");
const container = card.querySelector(".app-builds-container");
const noBuilds = card.querySelector("#no-builds");
// Pagination controls
const paginationControls = document.querySelector("#controls");
const paginationPrevious = document.querySelector("#up");
const paginationNext = document.querySelector("#down");
// Refresh variables
let buildRefreshTimeout;
const updateBuildsRefreshInterval = 5000;
/**
* Refresh the 'Builds' card
* @param {QueryParameters} options
*/
function load(options = {}) {
/** @type {QueryParameters} */
const params = Object.assign({}, options, { search: pageSearchInput.value });
// Avoid fetching if the page isn't active
if (document.hidden) {
return;
}
fetch(ajaxUrl + toQueryString(params)).then((rsp) => {
if (rsp.ok) {
rsp.text().then((responseText) => {
container.classList.remove("app-builds-container--loading");
pageSearch.classList.remove("jenkins-search--loading");
// Show the 'No builds' text if there are no builds
if (responseText.trim() === "") {
contents.innerHTML = "";
noBuilds.style.display = "block";
updateCardControls({
pageHasUp: false,
pageHasDown: false,
pageEntryNewest: false,
pageEntryOldest: false,
});
return;
}
// Show the refreshed builds list
contents.innerHTML = responseText;
noBuilds.style.display = "none";
behaviorShim.applySubtree(contents);
// Show the card controls
const div = document.createElement("div");
div.innerHTML = responseText;
const innerChild = div.children[0];
updateCardControls({
pageHasUp: innerChild.dataset.pageHasUp === "true",
pageHasDown: innerChild.dataset.pageHasDown === "true",
pageEntryNewest: innerChild.dataset.pageEntryNewest,
pageEntryOldest: innerChild.dataset.pageEntryOldest,
});
});
} else {
console.error("Failed to load 'Builds' card, response from API is:", rsp);
}
});
}
/**
* Shows/hides the card's pagination controls depending on the passed parameter
* @param {CardControlsOptions} parameters
*/
function updateCardControls(parameters) {
paginationControls.classList.toggle(
"jenkins-hidden",
!parameters.pageHasUp && !parameters.pageHasDown,
);
paginationPrevious.classList.toggle(
"app-builds-container__button--disabled",
!parameters.pageHasUp,
);
paginationNext.classList.toggle(
"app-builds-container__button--disabled",
!parameters.pageHasDown,
);
// We only want the list to refresh if the user is on the first page of results
if (!parameters.pageHasUp) {
createRefreshTimeout();
} else {
cancelRefreshTimeout();
}
buildHistoryPage.dataset.pageEntryNewest = parameters.pageEntryNewest;
buildHistoryPage.dataset.pageEntryOldest = parameters.pageEntryOldest;
}
paginationPrevious.addEventListener("click", () => {
load({ "newer-than": buildHistoryPage.dataset.pageEntryNewest });
});
paginationNext.addEventListener("click", () => {
cancelRefreshTimeout();
load({ "older-than": buildHistoryPage.dataset.pageEntryOldest });
});
function createRefreshTimeout() {
cancelRefreshTimeout();
buildRefreshTimeout = window.setTimeout(
() => load(),
updateBuildsRefreshInterval,
);
}
function cancelRefreshTimeout() {
if (buildRefreshTimeout) {
window.clearTimeout(buildRefreshTimeout);
buildRefreshTimeout = undefined;
}
}
const debouncedLoad = debounce(() => {
load();
}, 150);
document.addEventListener("DOMContentLoaded", function () {
pageSearchInput.addEventListener("input", function () {
container.classList.add("app-builds-container--loading");
pageSearch.classList.add("jenkins-search--loading");
debouncedLoad();
});
load();
});

View File

@ -0,0 +1,16 @@
/**
* @typedef QueryParameters
* @type {object}
* @property {string | undefined} search
* @property {string | undefined} older-than
* @property {string | undefined} newer-than
*/
/**
* @typedef CardControlsOptions
* @type {object}
* @property {boolean} pageHasUp
* @property {boolean} pageHasDown
* @property {string | undefined} pageEntryNewest
* @property {string | undefined} pageEntryOldest
*/

View File

@ -0,0 +1 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H15C15.5523 4 16 4.44772 16 5V10M10 16H5C4.44772 16 4 15.5523 4 15V10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"></path></svg>

After

Width:  |  Height:  |  Size: 258 B

View File

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512">
<circle cx="256" cy="256" r="45" fill="none" stroke="currentColor" stroke-miterlimit="10" stroke-width="32"/>
<circle cx="441" cy="256" r="45" fill="none" stroke="currentColor" stroke-miterlimit="10" stroke-width="32"/>
<circle cx="71" cy="256" r="45" fill="none" stroke="currentColor" stroke-miterlimit="10" stroke-width="32"/>
</svg>

After

Width:  |  Height:  |  Size: 427 B

View File

@ -244,7 +244,7 @@ $semantics: (
--pane-link-color--visited: black;
// Cards
--card-background: transparent;
--card-background: var(--background);
--card-background--hover: transparent;
--card-background--active: transparent;
--card-border-color: hsla(240, 25%, 75%, 0.25);

View File

@ -449,189 +449,6 @@ div.listview-jobs {
display: block;
}
/* ========================= build history ========================= */
#buildHistory a:visited {
color: fuchsia;
}
#buildHistory tr.no-wrap td.middle-align {
padding: 0;
}
#buildHistory .desc {
position: relative;
padding: 0;
margin-top: 5px;
white-space: normal;
color: var(--text-color-secondary);
word-break: break-word;
}
#buildHistory .build-row-cell {
position: relative;
}
#buildHistory .build-rss-links {
display: flex;
justify-content: end;
}
#buildHistory .build-rss-links a {
display: inline-flex;
align-items: center;
margin-right: 0.5rem;
}
#buildHistory .build-rss-all-icon,
#buildHistory .build-rss-failed-icon {
margin-right: 0.25rem;
svg {
width: 16px;
height: 16px;
}
}
#buildHistoryPage {
position: relative;
.build-search-row,
.build-search-no-results-row {
&:hover {
background: transparent !important;
}
td {
padding: 0 8px 8px;
}
}
.build-search-no-results-row {
border: none !important;
width: 100% !important;
background: transparent;
td {
padding-top: 0 !important;
}
}
}
#buildHistoryPageNav {
position: absolute;
right: -28px;
top: 96px;
border-radius: 6px;
background: var(--input-color);
border: 2px solid var(--input-border);
visibility: hidden;
z-index: 0;
opacity: 0;
transition: 0.2s ease;
// Invisible pseudo element on the left so #buildHistoryPageNav
// doesn't disappear when moving cursor over gap
&::before {
content: "";
position: absolute;
top: 0;
left: -8px;
bottom: 0;
width: 8px;
background: transparent;
z-index: -1;
}
.buildHistoryPageNav__item {
position: relative;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
padding: 0 8px;
height: 30px;
cursor: pointer;
transition: opacity 0.2s ease;
&:hover {
opacity: 0.5;
}
&:active {
opacity: 0.25;
}
&:not(:last-child) {
border-bottom: 2px solid rgba(0, 0, 0, 0.05);
}
&-page-one-top {
width: 12px;
height: 2px;
background: currentColor;
margin: 2px 0;
border-radius: 2px;
}
svg {
fill: currentColor;
width: 12px;
height: 12px;
}
}
}
#buildHistoryPageNav.mouseOverSidePanel {
visibility: visible;
right: -32px;
opacity: 1;
}
.build-row.model-link-active {
background: var(--light-grey) !important;
}
.build-row-cell {
font-size: var(--font-size-xs);
}
.build-row-cell .pane.build-name {
width: 25%;
font-weight: 500;
vertical-align: top;
}
.build-row-cell .pane.build-details {
width: 50%;
}
.build-row-cell .pane.build-controls {
width: 25%;
text-align: right;
}
.build-row-cell .pane.build-details.block {
width: 100%;
}
.pane.build-name a,
.pane.build-name a:visited {
color: var(--pane-link-color);
text-decoration: underline;
}
.pane.build-details a,
.pane.build-details a:visited {
color: var(--pane-link-color--visited);
opacity: 0.6;
text-decoration: none;
}
.pane.build-details a:hover {
opacity: 1;
text-decoration: underline;
}
/* ================ Element overflow calculation helper styles ================ */
.force-wrap,

View File

@ -0,0 +1,97 @@
$card-padding: 1rem;
.jenkins-card {
position: relative;
border-radius: 1rem;
margin-bottom: calc(var(--section-padding) / 2);
background: var(--card-background);
&__title {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 $card-padding;
height: 50px;
font-size: var(--font-size-sm) !important;
font-weight: 500;
width: 100%;
z-index: 1;
}
&__controls {
display: flex;
align-items: center;
justify-content: center;
gap: 0.4rem;
margin-right: -0.2rem;
}
&:not(:hover) {
.jenkins-card__unveil {
color: var(--text-color-secondary) !important;
}
}
&:hover {
.jenkins-card__reveal {
color: var(--text-color) !important;
}
}
&__content {
display: flex;
flex-direction: column;
padding: 0 $card-padding $card-padding;
color: var(--text-color-secondary);
&:empty {
display: none;
}
}
&::after {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
border: var(--card-border-width) solid var(--card-border-color);
z-index: 1;
pointer-events: none;
}
.jenkins-card__reveal {
display: flex;
align-items: center;
justify-content: center;
margin-block: -0.5rem;
min-height: 0;
padding: 0;
width: 26px;
height: 26px;
border-radius: 0.33rem;
color: var(--text-color-secondary) !important;
transition:
scale var(--standard-transition),
opacity var(--standard-transition);
svg {
width: 1rem;
height: 1rem;
transition: color var(--standard-transition);
}
&::before,
&::after {
opacity: 0;
}
&:hover {
opacity: 0.75;
}
&:active {
scale: 95%;
opacity: 0.5;
}
}
}

View File

@ -254,3 +254,18 @@ $dropdown-padding: 0.4rem;
border-radius: 50%;
}
}
.jenkins-jumplist-link {
appearance: none;
border: none;
background: none;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
svg {
width: 1.25rem;
height: 1.25rem;
}
}

View File

@ -2,8 +2,9 @@
@use "alert";
@use "badges";
@use "breadcrumbs";
@use "buttons-deprecated";
@use "buttons";
@use "buttons-deprecated";
@use "cards";
@use "content-blocks";
@use "dialogs";
@use "dropdowns";

View File

@ -10,7 +10,6 @@
var(--text-color-secondary) 25%,
transparent
);
margin-top: 2px;
display: block;
opacity: 1 !important;

View File

@ -90,165 +90,3 @@
#executors th.pane {
text-align: left;
}
/**
* Build history
*/
.build-row {
padding: 3px 4px;
}
.build-row.model-link-active {
background: var(--very-light-grey) !important;
}
.build-row-cell {
font-size: var(--font-size-xs);
}
.build-row-cell .pane.build-name {
width: 32%;
font-weight: 500;
vertical-align: top;
}
.build-row-cell .pane.build-details {
width: 50%;
}
.build-row-cell .pane.build-controls {
width: 18%;
text-align: right;
}
.build-row-cell .pane.build-details.block {
width: 100%;
}
.build-row.multi-line .build-row-cell .pane.build-name.block {
width: 100%;
}
.build-row-cell .pane.build-controls.block {
width: 100%;
}
.build-row-cell .pane.build-name .build-icon,
.build-row-cell .pane.build-name .display-name {
display: inline-block;
}
.build-row-cell .pane.build-name .build-icon {
position: absolute;
margin-top: 2px;
z-index: 1;
}
.build-row-cell .build-stop {
display: inline-block;
width: 30%;
}
.build-row-cell .build-badge {
display: inline-block;
text-align: right;
width: 70%;
padding: 2px 0;
}
.build-row-cell .build-badge > span {
display: inline-block;
max-width: 256px;
padding: 0 1px;
overflow: hidden;
}
.build-row-cell .build-badge > span + span {
margin: 0 0 0 2px !important;
}
@media (width >= 1170px) {
.build-row-cell .build-badge > span {
max-width: 296px;
}
}
.build-row .build-name-controls .pane.build-name,
.build-row .build-details-controls .pane.build-details {
width: 70%;
}
.build-row .build-row-cell .pane,
#side-panel .build-row .build-row-cell .pane {
padding: 0 2px; /* Sync changes with func expandControlsTo50Percent in hudson-behavior.js */
display: inline-block;
overflow: hidden;
}
.build-row.multi-line .build-row-cell .block {
display: block;
overflow: auto;
}
.build-row.multi-line .build-row-cell .indent-multiline {
margin-top: 5px;
}
.build-row.multi-line .build-row-cell .left-bar {
position: absolute;
top: 31px;
bottom: 10px;
left: 17px;
border-left: 1px solid var(--medium-grey);
}
.build-row-cell .pane.build-name .display-name {
margin-left: 20px;
word-break: break-all;
}
.build-row-cell .indent-multiline {
padding-left: 20px !important; /* Sync changes with func expandControlsTo50Percent in hudson-behavior.js */
}
.build-row.overflow-checked .build-row-cell {
visibility: visible;
}
.jenkins-pane {
&__information {
text-align: center;
line-height: 80px;
background-color: var(--panel-header-bg-color);
margin-top: 10px;
font-weight: 600;
border-radius: var(--form-input-border-radius);
}
.build-row {
transition: opacity 0.2s ease;
&-cell {
padding: 4px 8px;
}
}
&--loading .build-row {
opacity: 0.5;
}
}
.jenkins-pane__header--build-history {
display: grid;
grid-template-columns: auto 1fr auto;
font-weight: 500 !important;
.build-health-link {
margin: -9px -15px;
}
.jenkins-table__cell--tight {
width: auto;
margin-right: 1rem;
}
}

View File

@ -2,6 +2,7 @@
@use "build";
@use "dashboard";
@use "icon-legend";
@use "job";
@use "manage-jenkins";
@use "plugin-manager";
@use "setupWizardFirstUser";

View File

@ -0,0 +1,184 @@
@use "../abstracts/mixins";
#buildHistoryPage {
margin: 10px 0 10px 10px;
.jenkins-search {
margin-inline: -0.25rem;
margin-bottom: 5px;
}
}
.app-builds-container {
transition: opacity var(--standard-transition);
&__items {
margin-bottom: -0.5rem;
}
&__placeholder {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: 3rem;
animation: fade-in-builds-placeholder var(--standard-transition);
@keyframes fade-in-builds-placeholder {
from {
opacity: 0;
}
}
}
&__heading {
display: flex;
font-size: 0.75rem;
color: var(--text-color-secondary);
margin-top: 10px;
font-weight: 450;
margin-bottom: 4px;
}
&__controls {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
margin: 0 -0.35rem;
margin-top: 1rem;
margin-bottom: -0.5rem;
.jenkins-button {
padding: 10px;
svg {
transition: translate var(--standard-transition);
}
&:first-of-type {
justify-content: start;
&:hover {
translate: -2px 0;
svg {
translate: -4px 0;
}
}
}
&:last-of-type {
justify-content: end;
&:hover {
translate: 2px 0;
svg {
translate: 4px 0;
}
}
}
}
.app-builds-container__button--disabled {
color: var(--text-color-secondary) !important;
opacity: 0.25;
pointer-events: none;
}
}
&--loading {
opacity: 0.4;
filter: blur(0.5px);
}
}
.app-builds-container__item {
@include mixins.item();
display: grid;
grid-template-columns: auto 1fr auto;
gap: 0.5rem 0.65rem;
padding: 0 0 0.25rem;
margin: 0 -0.5rem;
font-size: 0.8125rem !important;
min-height: 2rem;
&__icon {
display: inline-flex;
justify-content: center;
padding: 0 0 0 0.5rem;
margin-top: 0.385rem;
svg {
width: 1.25rem;
height: 1.25rem;
}
}
.app-builds-container__item__inner {
display: flex;
align-items: stretch;
flex-wrap: wrap;
&__link {
display: flex;
color: var(--text-color);
gap: 0.5rem;
text-decoration: none;
font-weight: 450;
flex-grow: 1;
padding: 0.45rem 0 0;
.app-builds-container__item__time {
color: var(--text-color-secondary);
}
}
&__controls {
display: flex;
align-items: center;
justify-content: start;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 0.3rem;
}
}
&--not-interactable {
cursor: default;
&::before,
&::after {
display: none;
}
.app-builds-container__item__description {
margin-bottom: 0;
}
}
.jenkins-jumplist-link {
margin-top: 0.2rem;
padding-right: 0.8rem;
}
&__description {
color: var(--text-color-secondary);
padding-left: 2.25rem;
margin-top: -2px;
grid-column: 1 / span 2;
&::before {
content: "";
position: absolute;
left: 17px;
top: 34px;
bottom: 6px;
width: 2px;
background: var(--text-color-secondary);
border-radius: 10px;
opacity: 0.3;
}
}
}

View File

@ -2220,35 +2220,6 @@ function toQueryString(params) {
return query;
}
// eslint-disable-next-line no-unused-vars
function getElementOverflowParams(element) {
// First we force it to wrap so we can get those dimension.
// Then we force it to "nowrap", so we can get those dimension.
// We can then compare the two sets, which will indicate if
// wrapping is potentially happening, or not.
// Force it to wrap.
element.classList.add("force-wrap");
var wrappedClientWidth = element.clientWidth;
var wrappedClientHeight = element.clientHeight;
element.classList.remove("force-wrap");
// Force it to nowrap. Return the comparisons.
element.classList.add("force-nowrap");
var nowrapClientHeight = element.clientHeight;
try {
var overflowParams = {
element: element,
clientWidth: wrappedClientWidth,
scrollWidth: element.scrollWidth,
isOverflowed: wrappedClientHeight > nowrapClientHeight,
};
return overflowParams;
} finally {
element.classList.remove("force-nowrap");
}
}
// get the cascaded computed style value. 'a' is the style name like 'backgroundColor'
function getStyle(e, a) {
if (document.defaultView && document.defaultView.getComputedStyle) {

View File

@ -50,8 +50,8 @@ module.exports = (env, argv) => ({
"components/row-selection-controller": [
path.join(__dirname, "src/main/js/components/row-selection-controller"),
],
"filter-build-history": [
path.join(__dirname, "src/main/js/filter-build-history.js"),
"pages/project/builds-card": [
path.join(__dirname, "src/main/js/pages/project/builds-card.js"),
],
"simple-page": [path.join(__dirname, "src/main/scss/simple-page.scss")],
styles: [path.join(__dirname, "src/main/scss/styles.scss")],