diff --git a/changelog.d/1887.changed.md b/changelog.d/1887.changed.md new file mode 100644 index 000000000..ed6ca6c12 --- /dev/null +++ b/changelog.d/1887.changed.md @@ -0,0 +1 @@ +Improve dropdown filter UX: fix tag selection, prevent flicker during updates, and keep search results visible when selecting multiple items diff --git a/src/argus/htmx/incident/filter.py b/src/argus/htmx/incident/filter.py index 423eafba4..9a22ca050 100644 --- a/src/argus/htmx/incident/filter.py +++ b/src/argus/htmx/incident/filter.py @@ -86,19 +86,18 @@ class IncidentFilterForm(forms.Form): ) sourceSystemIds = forms.MultipleChoiceField( widget=SearchDropdownMultiSelect( - attrs={"placeholder": "search sources..."}, + attrs={"placeholder": "Select sources..."}, partial_get=None, - extra={"preload": True, "display_name": "sources"}, + extra={"preload": True, "display_name": "sources", "search_placeholder": "Search sources..."}, ), required=False, label="Sources", ) tags = forms.MultipleChoiceField( widget=SearchDropdownMultiSelect( - attrs={ - "placeholder": "search tags...", - }, + attrs={"placeholder": "Select tags..."}, partial_get=None, + extra={"search_placeholder": "Search tags..."}, ), required=False, label="Tags", @@ -147,8 +146,8 @@ def __init__(self, *args, **kwargs): # mollify tests partial_get = reverse("htmx:incident-filter") - self._init_source_field() - self._init_tag_field(*args, **kwargs) + self._init_source_field(partial_get) + self._init_tag_field(partial_get, *args, **kwargs) self.fields["source_types"].widget.partial_get = partial_get source_type_choices = SourceSystemType.objects.order_by("name").values_list("name", "name") @@ -161,29 +160,34 @@ def __init__(self, *args, **kwargs): self.fields["special_filters"].widget.partial_get = partial_get self.fields["special_filters"].choices = self.SPECIAL_FILTER_CHOICES - def _init_source_field(self): - """ - Initializes the 'sourceSystemIds' field widget for type-ahead search, - pre-loading all sources for client-side filtering. - """ - source_choices = SourceSystem.objects.order_by("name").values_list("id", "name") - self.fields["sourceSystemIds"].choices = tuple(source_choices) - - def _init_tag_field(self, *args, **kwargs): + def _init_tag_field(self, partial_get, *args, **kwargs): """ Initializes the 'tags' field widget and choices as key=value strings, and dynamically adds submitted tags. """ - self.fields["tags"].widget.partial_get = reverse("htmx:search-tags") + self.fields["tags"].widget.partial_get = partial_get + self.fields["tags"].widget.extra["search_url"] = reverse("htmx:search-tags") query_dict = args[0] if args else None if not query_dict: self.fields["tags"].choices = [] return - tags = query_dict.get("tags", []) + if hasattr(query_dict, "getlist"): + tags = query_dict.getlist("tags") + else: + tags = query_dict.get("tags", []) choices = [(tag, tag) for tag in tags] self.fields["tags"].choices = choices + def _init_source_field(self, partial_get): + """ + Initializes the 'sourceSystemIds' field widget for type-ahead search, + pre-loading all sources for client-side filtering. + """ + self.fields["sourceSystemIds"].widget.partial_get = partial_get + source_choices = SourceSystem.objects.order_by("name").values_list("id", "name") + self.fields["sourceSystemIds"].choices = tuple(source_choices) + def clean_tags(self): tags = self.cleaned_data["tags"] if not tags: diff --git a/src/argus/htmx/plannedmaintenance/views.py b/src/argus/htmx/plannedmaintenance/views.py index 9162b78ae..f5bc08021 100644 --- a/src/argus/htmx/plannedmaintenance/views.py +++ b/src/argus/htmx/plannedmaintenance/views.py @@ -85,7 +85,8 @@ class FilterWidgetMixin: def get_filter_widget(self): return SearchDropdownMultiSelect( partial_get=reverse("htmx:search-filters"), - attrs={"placeholder": "Search by filter name or user..."}, + attrs={"placeholder": "Select filters..."}, + extra={"search_placeholder": "Search by filter name or user..."}, ) def get_form(self, form_class=None): diff --git a/src/argus/htmx/templates/htmx/forms/checkbox_select_multiple.html b/src/argus/htmx/templates/htmx/forms/checkbox_select_multiple.html index 40e75c8c5..21701375f 100644 --- a/src/argus/htmx/templates/htmx/forms/checkbox_select_multiple.html +++ b/src/argus/htmx/templates/htmx/forms/checkbox_select_multiple.html @@ -6,7 +6,7 @@ {% if widget.wrap_label %} {{ widget.label }} diff --git a/src/argus/htmx/templates/htmx/forms/dropdown_select_multiple.html b/src/argus/htmx/templates/htmx/forms/dropdown_select_multiple.html index 43e87fa50..a29fa32eb 100644 --- a/src/argus/htmx/templates/htmx/forms/dropdown_select_multiple.html +++ b/src/argus/htmx/templates/htmx/forms/dropdown_select_multiple.html @@ -1,29 +1,53 @@ -