diff --git a/assets/scss/components/_forms.scss b/assets/scss/components/_forms.scss index 4d4e259..ad330d9 100644 --- a/assets/scss/components/_forms.scss +++ b/assets/scss/components/_forms.scss @@ -55,3 +55,11 @@ margin-top: .5rem; } } + +// Visible placeholder for search form +.search-input{ + border-color: $white; + &::placeholder{ + color: $gray-200; + } +} diff --git a/benevalibre/association/filters.py b/benevalibre/association/filters.py new file mode 100644 index 0000000..511f97d --- /dev/null +++ b/benevalibre/association/filters.py @@ -0,0 +1,13 @@ +from cruditor.filters import MultiCharFilter +from django_filters import FilterSet + +from . import forms, models + + +class AssociationIndexFilter(FilterSet): + search = MultiCharFilter(['name', 'description'], label="Filtrer") + + class Meta: + model = models.Association + form = forms.FilterForm + fields = ['search'] diff --git a/benevalibre/association/forms.py b/benevalibre/association/forms.py index c987e5c..f31b32b 100644 --- a/benevalibre/association/forms.py +++ b/benevalibre/association/forms.py @@ -80,3 +80,14 @@ class EngagementUpdateForm(CustomTapeformMixin, forms.ModelForm): class Meta: model = models.Engagement fields = ('role',) + + +class FilterForm(CustomTapeformMixin, forms.Form): + field_template = 'association/forms/fields/search.html' + field_label_css_class = 'sr-only' + widget_css_class = CustomTapeformMixin.widget_css_class + ' search-input' + + def apply_widget_options(self, field_name): + field = self.fields[field_name] + # utilise l'attribut placeholder au lieu du label + field.widget.attrs['placeholder'] = field.label diff --git a/benevalibre/association/tests/test_views.py b/benevalibre/association/tests/test_views.py index 1f41266..9d6fd20 100644 --- a/benevalibre/association/tests/test_views.py +++ b/benevalibre/association/tests/test_views.py @@ -117,6 +117,25 @@ class TestAssociationIndex: assert len(response.context['object_list']) == 3 assert count_text_in_content(response, "En modération") + def test_search(self, client): + Association.objects.create(name="Asso 4"), + + response = client.get(self.url, {'search': 'Asso'}) + assert response.status_code == 200 + assert len(response.context['object_list']) == 2 + assert count_text_in_content(response, "Asso 1") + assert count_text_in_content(response, "Asso 4") + + response = client.get(self.url, {'search': 'Asso 1'}) + assert response.status_code == 200 + assert len(response.context['object_list']) == 1 + assert count_text_in_content(response, "Asso 1") + assert not count_text_in_content(response, "Asso 4") + + response = client.get(self.url, {'search': 'Asso 6'}) + assert response.status_code == 200 + assert len(response.context['object_list']) == 0 + @pytest.mark.django_db class TestAssociationDetail: diff --git a/benevalibre/association/views.py b/benevalibre/association/views.py index 7384305..57f75ff 100644 --- a/benevalibre/association/views.py +++ b/benevalibre/association/views.py @@ -4,7 +4,7 @@ from django.contrib.auth.mixins import UserPassesTestMixin from django.http import Http404 from django.shortcuts import get_object_or_404 from django.urls import reverse -from django.views.generic import DetailView, ListView +from django.views.generic import DetailView from cruditor.views import ( CruditorAddView, @@ -12,12 +12,13 @@ from cruditor.views import ( CruditorDeleteView, CruditorListView, ) +from django_filters.views import FilterView from benevalibre.base.tables import EngagementIndexTable from benevalibre.utils.html import fa_icon from benevalibre.utils.views import CruditorPageMixin, PageMixin -from . import forms, models, tables +from . import filters, forms, models, tables def get_management_buttons(association, user): @@ -129,8 +130,7 @@ class AssociationRelatedFormMixin: # ASSOCIATIONS # ------------------------------------------------------------------------------ - -class AssociationIndex(PageMixin, ListView): +class AssociationIndex(PageMixin, FilterView): """ La page « Liste des associations » vous permet de voir les associations enregistrées et visibles publiquement au sein de l'application. En cliquant @@ -143,8 +143,9 @@ class AssociationIndex(PageMixin, ListView): page d'accueil si vous n'êtes pas connecté⋅e. """ - template_name = 'associations/association_list.html' + template_name = 'association/association_list.html' title = "Liste des associations" + filterset_class = filters.AssociationIndexFilter def get_queryset(self): return ( diff --git a/benevalibre/settings/base.py b/benevalibre/settings/base.py index d1da924..1858112 100644 --- a/benevalibre/settings/base.py +++ b/benevalibre/settings/base.py @@ -83,6 +83,7 @@ THIRD_PARTY_APPS = [ # crud & forms 'tapeforms', 'django_tables2', + 'django_filters', 'cruditor', 'docs', ] diff --git a/benevalibre/templates/association/association_list.html b/benevalibre/templates/association/association_list.html index 2b4ba21..9f18208 100644 --- a/benevalibre/templates/association/association_list.html +++ b/benevalibre/templates/association/association_list.html @@ -1,4 +1,11 @@ {% extends "base.html" %} +{% load tapeforms %} + +{% block titlebuttons %} +
+{% endblock %} {% block content %} {% if not object_list and not can_add_association %} diff --git a/benevalibre/templates/association/forms/fields/search.html b/benevalibre/templates/association/forms/fields/search.html new file mode 100644 index 0000000..3f06577 --- /dev/null +++ b/benevalibre/templates/association/forms/fields/search.html @@ -0,0 +1,12 @@ +{% extends "tapeforms/fields/bootstrap.html" %} + +{% block widget %} +