Skip to content

Commit 7514485

Browse files
authored
Toggleable compact view for suggestions lists (#848)
* feat: optional compact view for suggestions lists * feat: status change in compact view * feat: Implement compact mode for references & improve styling
1 parent 940f24e commit 7514485

File tree

15 files changed

+267
-122
lines changed

15 files changed

+267
-122
lines changed

src/shared/templates/base.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ <h1>
9090
{% block workflow %}
9191
<nav id="menu-bar">
9292
<ul class="row gap">
93-
<li class="row gap-small centered"><i class="icon-bin"></i><a href="{% url 'webview:suggestion:dismissed_suggestions' %}">Dismissed suggestions</a></li>
94-
<li class="row gap-small centered"><i class="icon-inbox"></i><a href="{% url 'webview:suggestion:untriaged_suggestions' %}">Untriaged suggestions</a></li>
93+
<li class="row gap-small centered"><i class="icon-bin"></i><a href="{% url 'webview:suggestion:dismissed_suggestions' %}?compact">Dismissed suggestions</a></li>
94+
<li class="row gap-small centered"><i class="icon-inbox"></i><a href="{% url 'webview:suggestion:untriaged_suggestions' %}?compact">Untriaged suggestions</a></li>
9595
<li class="row gap-small centered"><i class="icon-draft"></i><a href="{% url 'webview:suggestion:accepted_suggestions' %}">Accepted suggestions</a></li>
9696
<li class="row gap-small centered"><i class="icon-github"></i><a href="{% url 'webview:issue_list' %}">Published issues</a></li>
9797
</ul>
@@ -105,7 +105,7 @@ <h1>
105105
{% endblock layout %}
106106

107107
{% if is_paginated %}
108-
{% include "components/pagination.html" %}
108+
{% include "components/pagination.html" with is_compact=is_compact %}
109109
{% endif %}
110110

111111
<footer>

src/webview/static/colors.css

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,4 @@
1212
--light-green: #dbfcdb;
1313
--light-yellow: #fcfaba;
1414
--light-gray: #eee;
15-
16-
--foreground: black;
17-
--background: white;
1815
}

src/webview/static/utility.css

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,6 @@
2424
margin: 1em 0;
2525
}
2626

27-
.small {
28-
font-size: 0.75rem;
29-
}
30-
31-
.tag {
32-
border: solid 2px var(--background);
33-
border-radius: 0.4em;
34-
padding: 0.2rem 0.4rem;
35-
outline-width: 2px;
36-
word-break: normal;
37-
display: inline-block;
38-
}
39-
40-
.fill-gray {
41-
color: var(--background);
42-
background-color: var(--gray);
43-
}
44-
4527
/* Buttons
4628
*
4729
* .btn - Base button style to be used on every standard button
@@ -110,6 +92,27 @@
11092
outline-style: solid;
11193
}
11294

95+
/* Tags
96+
*
97+
* .tag - Base tag style to be used on every tag
98+
* .tag-gray - Gray tag variant
99+
*
100+
* Usage: Combine .tag with one of the color variants
101+
* Example: <span class="tag tag-gray">Keyword</button>
102+
*/
103+
104+
.tag {
105+
border-radius: 1000px;
106+
padding: 0em 0.4em;
107+
word-break: normal;
108+
}
109+
110+
.tag-gray {
111+
color: black;
112+
border: 1px solid var(--gray);
113+
background-color: var(--light-gray);
114+
}
115+
113116
/* Titles
114117
*
115118
* .page-title - Only for the main title of a page
@@ -137,6 +140,7 @@
137140
* .condensed - Apply condensed font
138141
* .uppercase - Force uppercase
139142
* .dimmed - Dimmed gray text color
143+
* .no-line-breaks - Prevent line breaks
140144
*/
141145

142146
.bold {
@@ -159,6 +163,10 @@
159163
color: var(--gray);
160164
}
161165

166+
.no-line-breaks {
167+
white-space: nowrap;
168+
}
169+
162170
/* Layout
163171
*
164172
* Layout utilities using flexbox. Start with a required container type,
@@ -183,12 +191,17 @@
183191
* .gap-small - Small gap between items
184192
* .dividers - Display a separation line between items (handles gaps, don't use .gap or .gap-small)
185193
*
194+
* Horizontal separator line:
195+
* .divider - On an <hr> element: display a horizontal line to separate content.
196+
* To be used in a column + (small-)gap container.
197+
*
186198
* Sizing:
187199
* .full-width - Force to use all available space (width: 100%)
188200
* .grow - Make an item grow in a container
189201
* .spread - Force items to be spread across available space
190202
* For instance 2 elements on a .row .spread .full-width will be respectively on the far left and far right.
191203
* .wrap - Allow items to wrap for responsivity. This is how responsivity is to be implemented rather than through breakpoints.
204+
*
192205
*/
193206

194207
.row {
@@ -234,6 +247,10 @@
234247
width: 100%;
235248
}
236249

250+
.justify-right {
251+
justify-content: flex-end;
252+
}
253+
237254
.spread {
238255
justify-content: space-between;
239256
}
@@ -270,6 +287,13 @@
270287
border-bottom: none;
271288
}
272289

290+
.divider {
291+
width: 100%;
292+
height: 1px;
293+
background-color: var(--gray);
294+
border: none;
295+
}
296+
273297
/* Size */
274298

275299
.w-12 {
@@ -288,6 +312,7 @@
288312
*
289313
* .rounded-box - Bordered container with padding and rounded corners.
290314
* Includes overflow: auto for locally scrollable content.
315+
* .rounded-box-compact - Same with small padding (same as buttons) to be used for inline boxes next to buttons for instance.
291316
*/
292317

293318
.rounded-box {
@@ -297,6 +322,13 @@
297322
overflow: auto;
298323
}
299324

325+
.rounded-box-compact {
326+
border: 1px solid var(--gray);
327+
padding: 0.3em;
328+
border-radius: 0.4em;
329+
overflow: auto;
330+
}
331+
300332
/* Toggle box
301333
*
302334
* Big toggler element with visual state indicators:

src/webview/suggestions/context/types.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class SuggestionStubContext:
4040
issue_link: str | None
4141
undo_status_target: str | None # FIXME(@florentc): change to the real enum type
4242
show_affected_product: bool = False
43+
is_compact: bool = False
4344

4445

4546
# Maintainers
@@ -88,9 +89,11 @@ def __init__(
8889
self,
8990
suggestion: CVEDerivationClusterProposal,
9091
can_edit: bool,
92+
is_compact: bool,
9193
) -> None:
9294
self.show_status: bool = True
9395
self.can_edit: bool = can_edit
96+
self.is_compact: bool = is_compact
9497
self.suggestion: CVEDerivationClusterProposal = suggestion
9598
self.suggestion_stub_context: SuggestionStubContext | None = None
9699
self.update_package_list_context(can_edit=can_edit)
@@ -172,6 +175,12 @@ def update_maintainer_list_context(
172175
can_edit=can_edit,
173176
suggestion_id=self.suggestion.pk,
174177
# FIXME(@fricklerhandwerk): It's really opaque what this is for.
178+
# NOTE(@florentc): This is simply the context, meaning the data
179+
# necessary to render the template, for the "add maintainer"
180+
# widget. This follows the xxxxxx_context naming convention. The
181+
# widget requires the suggestion id to point to the right action
182+
# endpoint, and the optional error message to render next to it for
183+
# feedback.
175184
maintainer_add_context=MaintainerAddContext(
176185
self.suggestion.pk, maintainer_add_error_message
177186
),

src/webview/suggestions/views/base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@ def fetch_suggestion(suggestion_id: int) -> CVEDerivationClusterProposal:
2121
def get_suggestion_context(
2222
suggestion: CVEDerivationClusterProposal,
2323
can_edit: bool,
24+
is_compact: bool = False,
2425
) -> SuggestionContext:
2526
return SuggestionContext(
2627
suggestion=suggestion,
2728
can_edit=can_edit,
29+
is_compact=is_compact,
2830
)
2931

3032

src/webview/suggestions/views/lists.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ class SuggestionListView(ListView, ABC):
2323
status_filter: CVEDerivationClusterProposal.Status | None = None
2424
package_filter: str | None = None # To be defined in concrete classes
2525

26+
@property
27+
def is_compact(self) -> bool:
28+
"""Whether to show compact suggestions in the list"""
29+
compact_param = self.request.GET.get("compact")
30+
if compact_param is not None:
31+
return compact_param.lower() not in ("false", "0", "no", "off")
32+
else:
33+
return False
34+
2635
def get_queryset(self) -> QuerySet[CVEDerivationClusterProposal]:
2736
self.package_filter = self.kwargs.get("package_name")
2837
if self.status_filter is None:
@@ -63,9 +72,12 @@ def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
6372
# Convert suggestions to SuggestionContext objects for the current page
6473
suggestion_contexts = []
6574
can_edit = can_edit_suggestion(self.request.user)
75+
is_compact = self.is_compact
6676
# FIXME(@fricklerhandwerk): This is very slow (scales with number of events in the activity log), it should batch all related queries and do the wiring in Python.
6777
for suggestion in page_obj.object_list:
68-
suggestion_context = get_suggestion_context(suggestion, can_edit=can_edit)
78+
suggestion_context = get_suggestion_context(
79+
suggestion, can_edit=can_edit, is_compact=is_compact
80+
)
6981
suggestion_context.show_status = (
7082
self.status_filter
7183
is None # We don't show status in lists already filtered by status
@@ -78,6 +90,7 @@ def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
7890
"page_obj": page_obj,
7991
"status_filter": self.status_filter,
8092
"package_filter": self.package_filter,
93+
"is_compact": self.is_compact,
8194
"adjusted_elided_page_range": paginator.get_elided_page_range(
8295
page_obj.number
8396
),

src/webview/suggestions/views/status.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,22 @@ def post(self, request: HttpRequest, suggestion_id: int) -> HttpResponse:
3333
if not request.user or not can_edit:
3434
return HttpResponseForbidden()
3535

36-
# Get suggestion context
37-
suggestion = fetch_suggestion(suggestion_id)
38-
suggestion_context = get_suggestion_context(suggestion, can_edit=can_edit)
39-
4036
# Get form data
4137
new_status = request.POST.get("new_status")
4238
new_comment = request.POST.get("comment", "").strip()
39+
is_compact = (
40+
request.POST.get("is_compact") == "true"
41+
) # To maintain compact/detailed during live reload
4342
undo_status_change = request.POST.get(
4443
"undo_status_change"
4544
) # This is set if the status change comes from clicking "Undo"
4645

46+
# Get suggestion context
47+
suggestion = fetch_suggestion(suggestion_id)
48+
suggestion_context = get_suggestion_context(
49+
suggestion, can_edit=can_edit, is_compact=is_compact
50+
)
51+
4752
# Validate status change
4853
if not new_status:
4954
return self._handle_error(request, suggestion_context, "Missing new status")
@@ -130,6 +135,7 @@ def post(self, request: HttpRequest, suggestion_id: int) -> HttpResponse:
130135
suggestion,
131136
issue_link=github_issue_link,
132137
undo_status_target=undo_status_target,
138+
is_compact=is_compact,
133139
)
134140
elif new_status == "published":
135141
# NOTE(@florentc): This treats the case where we are in detail view

src/webview/templates/components/affected_products.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<div>
88
{{ package_name }}
99
</div>
10-
<ul class="w-12">
10+
<ul class="{% if is_compact %}row gap-small wrap{% else %}w-12{% endif %}">
1111
{% for status, vc_str in ap.version_constraints %}
1212
<li>
1313
<span

src/webview/templates/components/cve_references.html

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
{% load viewutils %}
22

3-
<div class="rounded-box">
4-
<h2 class="heading dimmed">References</h2>
5-
<ul class="column gap">
3+
<div class="{% if not is_compact %}rounded-box{% endif %}">
4+
{% if not is_compact %}<h2 class="heading dimmed">References</h2>{% endif %}
5+
<ul class="column {% if is_compact %}gap-small{% else %}gap{% endif %}">
66
{% for ref in references %}
7-
<li class="row baseline gap">
8-
<div>
7+
<li class="row baseline gap-small wrap">
98
{% if ref.name %}
109
<a href="{{ref.url}}" target="_blank">{{ref.name}}</a>
1110
{% else %}
12-
<a href="{{ref.url}}" target="_blank">{{ref.url}}</a>
11+
{{ref.url|urlizetrunc:80}}
1312
{% endif %}
14-
{% for tag in ref.tags %}
15-
<span class="tag fill-gray">{{tag.value}}</span>
16-
{% endfor %}
17-
</div>
13+
{% for tag in ref.tags %}
14+
<span class="tag tag-gray">{{tag.value}}</span>
15+
{% endfor %}
1816
</li>
1917
{% endfor %}
2018
</ul>

src/webview/templates/components/pagination.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
{% if page_number == paginator.ELLIPSIS %}
55
{{page_number}}
66
{% else %}
7-
<a href="{{request.path}}?page={{page_number}}" class="{% if page_number == page_obj.number %}bold{% endif %}">
7+
<a href="{{request.path}}?page={{page_number}}{% if is_compact %}&compact{% endif %}" class="{% if page_number == page_obj.number %}bold{% endif %}">
88
{{page_number}}
99
</a>
1010
{% endif %}

0 commit comments

Comments
 (0)