Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 19 additions & 12 deletions hypha/apply/categories/blocks.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django import forms
from django.db.models import BLANK_CHOICE_DASH
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
from wagtail.blocks import (
Expand All @@ -9,8 +10,13 @@
)
from wagtail.coreutils import resolve_model_string

from hypha.apply.funds.widgets import ChoicesSelectMultipleWidget, ChoicesSelectWidget
from hypha.apply.stream_forms.blocks import OptionalFormFieldBlock

# Above this many options a radio/checkbox list stops being usable, so fall back
# to a searchable Choices.js select.
SEARCHABLE_SELECT_THRESHOLD = 32


class ModelChooserBlock(ChoiceBlock):
# Implement this block as it's referenced in the old migrations.
Expand Down Expand Up @@ -65,23 +71,24 @@ def get_field_kwargs(self, struct_value):
kwargs = super().get_field_kwargs(struct_value)
category = self.get_instance(id=struct_value["category"])
kwargs = self.use_defaults_from_category(kwargs, category)
choices = category.options.values_list("id", "value")
choices = list(category.options.values_list("id", "value"))
if not struct_value["multi"] and len(choices) >= SEARCHABLE_SELECT_THRESHOLD:
# A <select> auto-selects its first option, so offer an empty one.
choices.insert(0, BLANK_CHOICE_DASH[0])
kwargs.update({"choices": choices})
return kwargs

def get_widget(self, struct_value):
category = self.get_instance(id=struct_value["category"])
# Pick widget according to number of options to maintain good usability.
many_options = category.options.count() >= SEARCHABLE_SELECT_THRESHOLD
if struct_value["multi"]:
category = self.get_instance(id=struct_value["category"])
category_size = category.options.count()
# Pick widget according to number of options to maintain good usability.
if category_size < 32:
return forms.CheckboxSelectMultiple
else:
from hypha.apply.funds.tables import MultiCheckboxesWidget

return MultiCheckboxesWidget
else:
return forms.RadioSelect
return (
ChoicesSelectMultipleWidget
if many_options
else forms.CheckboxSelectMultiple
)
return ChoicesSelectWidget if many_options else forms.RadioSelect

def prepare_data(self, value, data, serialize):
if not data:
Expand Down
6 changes: 3 additions & 3 deletions hypha/apply/funds/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from .models.co_applicants import CoApplicantProjectPermission, CoApplicantRole
from .permissions import can_change_external_reviewers
from .utils import model_form_initial, render_icon
from .widgets import MultiCheckboxesWidget
from .widgets import ChoicesSelectMultipleWidget


class ApplicationSubmissionModelForm(forms.ModelForm):
Expand Down Expand Up @@ -239,7 +239,7 @@ class BatchUpdateReviewersForm(forms.Form):
)
external_reviewers = forms.ModelMultipleChoiceField(
queryset=User.objects.reviewers().only("pk", "full_name"),
widget=MultiCheckboxesWidget(attrs={"data-placeholder": _("Select...")}),
widget=ChoicesSelectMultipleWidget(attrs={"data-placeholder": _("Select...")}),
label=_("External Reviewers"),
required=False,
)
Expand Down Expand Up @@ -386,7 +386,7 @@ def __init__(self, *args, choices_groupby, **kwargs):
class UpdateMetaTermsForm(ApplicationSubmissionModelForm):
meta_terms = GroupedModelMultipleChoiceField(
queryset=None, # updated in init method
widget=MultiCheckboxesWidget(attrs={"data-placeholder": _("Select...")}),
widget=ChoicesSelectMultipleWidget(attrs={"data-placeholder": _("Select...")}),
label=_("Tags"),
choices_groupby="get_parent",
required=False,
Expand Down
4 changes: 2 additions & 2 deletions hypha/apply/funds/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from hypha.core.tables import RelativeTimeColumn

from .models import ApplicationSubmission, Round, ScreeningStatus
from .widgets import MultiCheckboxesWidget
from .widgets import ChoicesSelectMultipleWidget
from .workflows import STATUS_SLUGS, STATUSES

User = get_user_model()
Expand Down Expand Up @@ -192,7 +192,7 @@ class MultiCheckboxesMixin(filters.Filter):
def __init__(self, *args, **kwargs):
label = kwargs.get("label")
kwargs.setdefault(
"widget", MultiCheckboxesWidget(attrs={"data-placeholder": label})
"widget", ChoicesSelectMultipleWidget(attrs={"data-placeholder": label})
)
super().__init__(*args, **kwargs)

Expand Down
14 changes: 11 additions & 3 deletions hypha/apply/funds/widgets.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
from django import forms


class MultiCheckboxesWidget(forms.SelectMultiple):
class ChoicesJSMixin:
"""
Custom widget for Choices.js. Adds the required attributes.
Adds the attributes required to initialise Choices.js on a select.
"""

def __init__(self, *args, **kwargs):
attrs = kwargs.get("attrs", {})
# Add the date attribute for Choices.js initialization
# Add the data attributes for Choices.js initialization
attrs.setdefault("data-js-choices", "")
attrs.setdefault("data-placeholder", "")
kwargs["attrs"] = attrs
super().__init__(*args, **kwargs)


class ChoicesSelectWidget(ChoicesJSMixin, forms.Select):
pass


class ChoicesSelectMultipleWidget(ChoicesJSMixin, forms.SelectMultiple):
pass
4 changes: 2 additions & 2 deletions hypha/apply/projects/forms/invoice.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from django.utils.translation import gettext_lazy as _
from django_file_form.forms import FileFormMixin

from hypha.apply.funds.widgets import MultiCheckboxesWidget
from hypha.apply.funds.widgets import ChoicesSelectMultipleWidget
from hypha.apply.stream_forms.fields import MultiFileField, SingleFileField

from ..models.invoice import (
Expand Down Expand Up @@ -233,7 +233,7 @@ def clean_invoices(self):
class InvoiceTagsForm(forms.ModelForm):
tags = forms.ModelMultipleChoiceField(
queryset=InvoiceTag.objects.all(),
widget=MultiCheckboxesWidget,
widget=ChoicesSelectMultipleWidget,
required=False,
label=_("Tags"),
)
Expand Down