[RFC PATCH 2/4] patch-detail: add patch relations table
Raxel Gutierrez
raxel at google.com
Tue Aug 24 00:58:45 AEST 2021
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 <raxel at 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 at 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
More information about the Patchwork
mailing list