The motivation behind the patch relations table is to be able to provide information about a given version of a patch in relation to its other versions. The table indicates whether there exists unaddressed comments in other versions and can also show what's the most up-to-date version of a given patch. The information on what is unresolved throughout the related patches history helps reviewers and submitters keep track of and quickly locate what needs to be done for a patch. Image [1] for reference. This involves:
- Add and style patch relations table with a summary row that shows the total unaddressed and addressed comments for the patch relation and the existence of various tags through their abbreviations (e.g. A/R/T). - Add collapse button that hides all patches except for the current patch (which is highlighted) and the patch relation summary row [2]. If the patch relation contains errors, users can use the "Wrong related patches?" link to address the issues. For users with patch edit permissions, the patch relation can be modified (i.e. add/remove patches) [3]. For users without permission, an email is prompted to email the maintainers of the project to ask them to fix the patch relation. However, the functionality to add/remove patches is introduced in an upcoming patch. Something that is left TODO is fix the email prompt to maintainers to include the mailing list for each specific project instead of the patchwork mailing list. Also, commas and whitespace characters from the patch subject are cut off in the subject line of the email which needs to be fixed. [1] https://i.imgur.com/cwYKiUx.png [2] https://i.imgur.com/4HGCTYp.png [3] https://i.imgur.com/Xyy18oL.png Signed-off-by: Raxel Gutierrez <ra...@google.com> --- htdocs/css/style.css | 120 +++++++++++++++++- htdocs/js/submission.js | 41 ++++++ patchwork/templates/patchwork/submission.html | 92 ++++++++++++++ 3 files changed, 252 insertions(+), 1 deletion(-) diff --git a/htdocs/css/style.css b/htdocs/css/style.css index 9156aa6e..89c0f5c0 100644 --- a/htdocs/css/style.css +++ b/htdocs/css/style.css @@ -1,8 +1,14 @@ :root { --light-color:rgb(247, 247, 247); - --success-color:rgb(92, 184, 92); + --dark-color:rgb(41, 43, 44); + --dark-color-75:rgb(41, 43, 44, .75); + --dark-color-50:rgb(41, 43, 44, .5); + --dark-color-25:rgb(41, 43, 44, .25); --warning-color:rgb(240, 173, 78); + --success-color:rgb(92, 184, 92); + --success-color-75:rgb(92, 184, 92, .75); --danger-color:rgb(217, 83, 79); + --danger-color-75:rgb(217, 83, 79, .75); } h2 { @@ -301,6 +307,118 @@ table.patch-meta tr th, table.patch-meta tr td { color: #f7977a; } +.table-bl-border-radius { + border-bottom-left-radius: 4px +} + +.table-br-border-radius{ + border-bottom-right-radius: 4px +} + +.panel { + width: fit-content; + margin-top: 16px; +} + +.panel-heading { + display: flex; + align-items: baseline; + justify-content: space-between; + font-weight: bold; + padding: 8px; +} + +#table-collapse-up-btn, #table-collapse-down-btn { + font-size: 16px; + color: var(--dark-color); + cursor: pointer; + margin-right: 4px; +} + +#table-collapse-up-btn:hover, #table-collapse-down-btn:hover { + color: var(--dark-color-75); +} + +.panel-actions-bar { + display: flex; + flex-wrap: wrap; + align-items: center; +} + +#related-patch-actions-input { + font-weight: normal; + border: var(--dark-color-25) solid 1px; + border-radius: 4px; + background-color: white; + margin-left: 8px; + padding: 2px 8px; +} + +#related-patch-actions-input:focus { + outline: none; +} + +#add-patch-relation-btn { + color: var(--success-color); + font-size: 16px; + margin: 0px 8px; + cursor: pointer; +} + +#add-patch-relation-btn:hover { + color: var(--success-color-75); +} + +#remove-patch-relation-btn { + color: var(--danger-color); + font-size: 16px; + margin-right: 8px; + cursor: pointer; +} + +#remove-patch-relation-btn:hover { + color: var(--danger-color-75); +} + +#related-patches-table { + border: inherit; + border-collapse: separate; + border-radius: 0px 0px 4px 4px; +} + +.btn-copy { + background-color: var(--light-color); + border-radius: 4px; + transition: all 0.3s ease-in-out; +} + +.btn-copy:hover { + outline: none; + transform: translateY(-2px); + filter: opacity(75%) drop-shadow(0px 2px 2px rgba(0, 0, 0, 0.4)); +} + +button[title="Copy to Clipboard"].btn-copy:active { + outline: none; + transform: translateY(-1px); + filter: opacity(100%); +} + +#current-related-patch { + font-weight: bold; +} + +.related-patches-header, .related-patches-footer { + border: transparent solid 1px; + border-radius: 4px 4px 0px 0px; + font-weight: 600; +} + +.table thead > .related-patches-header > th { + vertical-align: middle; + border-bottom: var(--dark-color-50) solid 1px; +} + .submission-message .meta { display: flex; align-items: center; diff --git a/htdocs/js/submission.js b/htdocs/js/submission.js index 47cffc82..23f73233 100644 --- a/htdocs/js/submission.js +++ b/htdocs/js/submission.js @@ -2,6 +2,15 @@ import { updateProperty } from "./rest.js"; $( document ).ready(function() { const patchMeta = document.getElementById("patch-meta"); + const actionsInput = document.getElementById("related-patch-actions-input"); + const collapseTableBtn = document.getElementById("table-collapse-up-btn"); + const expandTableBtn = document.getElementById("table-collapse-down-btn"); + const relatedActionsBar = document.getElementsByClassName("panel-actions-bar")[0]; + const maintainers = django_maintainers_data['maintainers']; + + // Resize related patches input to length of placeholder text (+1 accounts for last letter) + actionsInput.setAttribute('size', actionsInput.getAttribute('placeholder').length + 1); + function toggleDiv(link_id, headers_id, label_show, label_hide) { const link = document.getElementById(link_id) const headers = document.getElementById(headers_id) @@ -34,6 +43,38 @@ $( document ).ready(function() { }) }); + function toggleVisibility(elements) { + for (let elem of elements) { + elem.classList.toggle("hidden"); + } + } + + $("#patch-relation-issue").click((event) => { + if (relatedActionsBar.classList.contains("hidden") && is_editable) { + $(relatedActionsBar).toggleClass("hidden"); + $(event.target).toggleClass("hidden"); + event.preventDefault(); + } else if (!is_editable) { + // TODO: Fix commas (',') cutting off rest of patch subject + const patchSubject = $("#current-related-patch > td > a").text().trim(); + let maintainersList = ""; + for (let i = 0; i < maintainers.length; i++) { + if (i != maintainers.length-1) { + maintainersList += maintainers[i] + ";" + } else { + maintainersList += maintainers[i]; + } + } + event.target.href = `mailto:patchwork@lists.ozlabs.org?subject=[Patch Relations Fix]:%20${patchSubject}&cc=${maintainersList}` + } + }); + + // Click listener to collapse/expand related patches table + $(collapseTableBtn).add(expandTableBtn).click(function() { + const collapseRows = document.querySelectorAll("#related-patches-body > tr:not(.related-patches-footer, #current-related-patch)"); + toggleVisibility([...collapseRows, collapseTableBtn, expandTableBtn]); + }); + // Click listener to show/hide headers document.getElementById("toggle-patch-headers").addEventListener("click", function() { toggleDiv("toggle-patch-headers", "patch-headers"); diff --git a/patchwork/templates/patchwork/submission.html b/patchwork/templates/patchwork/submission.html index 7dd6ae97..3c1d7bed 100644 --- a/patchwork/templates/patchwork/submission.html +++ b/patchwork/templates/patchwork/submission.html @@ -139,6 +139,98 @@ {% endif %} </table> +<div class="panel panel-default"> + <div class="panel-heading"> + <div class="panel-title"> + <span id="table-collapse-up-btn" class="glyphicon glyphicon glyphicon-collapse-up" aria-hidden="true"></span> + <span id="table-collapse-down-btn" class="glyphicon glyphicon-collapse-down hidden" aria-hidden="true"></span> + Patch Relations + </div> + <form method="POST" class="panel-actions"> + {% csrf_token %} + <a id="patch-relation-issue" href="">Wrong related patches?</a> + <div class="panel-actions-bar hidden"> + <input type="text" name="related_input" id="related-patch-actions-input" placeholder="Add/remove with comma-separated IDs and Message IDs"> + <button type="submit" name="action" id="add-patch-relation-btn" class="glyphicon glyphicon-plus-sign" value="add-related"></button> + <button type="submit" name="action" id="remove-patch-relation-btn" class="glyphicon glyphicon-minus-sign" value="remove-related"></button> + </div> + </form> + </div> + <table id="related-patches-table" class="table"> + <thead> + <tr class="related-patches-header"> + <th class="table-tl-border-radius">ID</th> + <th>Patch</th> + <th title="Total ( Unaddressed / Addressed )">Comments</th> + <th title="Acked-by / Reviewed-by / Tested-by" class="table-tr-border-radius">A/R/T</th> + </tr> + </thead> + <tbody id="related-patches-body"> + {% for related_patch in related_same_project %} + {% if related_patch.name != submission.name %} + <tr class="related-patch-row"> + {% else %} + <tr id="current-related-patch" class="related-patch-row bg-warning"> + {% endif %} + {% if patch_relation %} + <td> + {% else %} + <td class="table-bl-border-radius"> + {% endif %} + <button type="button" class="btn btn-xs btn-copy" + data-clipboard-text="{{ related_patch.id }}" title="Copy to Clipboard"> + {{ related_patch.id }} + </button> + </td> + <td> + <a href="{% url 'patch-detail' project_id=project.linkname msgid=related_patch.url_msgid %}"> + {{ related_patch.name|default:"[no subject]"|truncatechars:100 }} + </a> + </td> + <td> + <span class="total-comments-count" title="Total Comments"> + {{ related_patch.unaddressed_comments_count|add:related_patch.addressed_comments_count }} + </span> ( + <span title="Unaddressed Comments"> + <span id="comment-action-icon" class="glyphicon glyphicon-warning-sign text-warning" aria-hidden="true"></span> + <span class="unaddressed-comments-count"> + {{ related_patch.unaddressed_comments_count }} + </span> + </span> + <span title="Addressed Comments"> + <span id="comment-status-icon" class="glyphicon glyphicon-ok-circle text-success" aria-hidden="true"></span> + <span class="addressed-comments-count"> + {{ related_patch.addressed_comments_count }} + </span>) + </span> + </td> + {% if patch_relation %} + <td>{{ related_patch|patch_tags }}</td> + {% else %} + <td class="table-br-border-radius">{{ related_patch|patch_tags }}</td> + {% endif %} + </tr> + {% endfor %} + {% if patch_relation %} + <tr class="related-patches-footer bg-info"> + <td></td> + <td class="table-bl-border-radius"> + {{ related_same_project|length }} Related Patches + </td> + <td> + {{ patch_relation.unaddressed_comments_total|add:patch_relation.addressed_comments_total }} ( + <span id="comment-action-icon" class="glyphicon glyphicon-warning-sign text-warning" aria-hidden="true"></span> + {{ patch_relation.unaddressed_comments_total }} + <span id="comment-status-icon" class="glyphicon glyphicon-ok-circle text-success" aria-hidden="true"></span> + {{ patch_relation.addressed_comments_total }}) + </td> + <td class="table-br-border-radius">{{ related_same_project|patch_relation_tags:project }}</td> + </tr> + {% endif %} + </tbody> + </table> +</div> + <div class="patchforms"> {% if patchform %} <div class="patchform patchform-properties"> -- 2.33.0.rc2.250.ged5fa647cd-goog _______________________________________________ Patchwork mailing list Patchwork@lists.ozlabs.org https://lists.ozlabs.org/listinfo/patchwork